忆梦|我擦~字符串转字节切片后,切片的容量竟然千奇百怪( 三 )
综合上述信息我们得出的结论是 , 编译器会对stringtoslicebyte的函数调用生成一个AST(抽象语法树)对应的节点 。 因此我们也知道传递给stringtoslicebyte函数的第一个变量也就对应于上图中的变量a.
其中a的初始值为nodnil()的返回值 , 即默认为nil. 但是n.Esc == EscNone时 , a会变成一个数组 。 我们看一下EscNone的解释.
// 此代码位于cmd/compile/internal/gc/esc.go中const ( // ... EscNone// Does not escape to heap, result, or parameters....)由上可知, EscNone用来判断变量是否逃逸,到这儿了我们就很好办了 , 接下来我们对现象一和现象二的代码进行逃逸分析.
# 执行变量逃逸分析命令: go run -gcflags '-m -l' test.go# 现象一逃逸分析如下:./test.go:7:14: ([]byte)(a) escapes to heap./test.go:8:13: main ... argument does not escape./test.go:8:13: bs escapes to heap./test.go:8:21: len(bs) escapes to heap./test.go:8:30: cap(bs) escapes to heap[97 98 99] 3 8# 现象二逃逸分析如下:./test.go:7:14: main ([]byte)(a) does not escape./test.go:8:13: main ... argument does not escape./test.go:8:17: len(bs) escapes to heap./test.go:8:26: cap(bs) escapes to heap3 32根据上面的信息我们知道在现象一中 , bs变量发生了逃逸 , 现象二中变量未发生逃逸 , 也就是说stringtoslicebyte函数的第一个参数在变量未发生逃逸时其值不为nil,变量发生逃逸时其值为nil 。 到这里我们已经搞明白stringtoslicebyte的第一个参数了 ,那我们继续分析stringtoslicebyte的内部逻辑
我们在runtime/string.go中看到stringtoslicebyte第一个参数的类型定义如下:
const tmpStringBufSize = 32type tmpBuf [tmpStringBufSize]byte综上: 现象二中bs变量未发生变量逃逸, stringtoslicebyte第一个参数不为空且是一个长度为32的byte数组, 因此在现象二中生成了一个容量为32的切片
根据对stringtoslicebyte的源码分析 ,我们知道现象一调用了rawbyteslice函数
func rawbyteslice(size int) (b []byte) { cap := roundupsize(uintptr(size)) p := mallocgc(cap, nil, false) if cap != uintptr(size) {memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size)) } *(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)} return}由上面的代码知道 ,切片的容量通过runtime/msize.go中的roundupsize函数计算得出, 其中_MaxSmallSize和class_to_size均定义在runtime/sizeclasses.go
func roundupsize(size uintptr) uintptr { if size < _MaxSmallSize {if size <= smallSizeMax-8 {return uintptr(class_to_size[size_to_class8[(size+smallSizeDiv-1)/smallSizeDiv]])} else {return uintptr(class_to_size[size_to_class128[(size-smallSizeMax+largeSizeDiv-1)/largeSizeDiv]])} } if size+_PageSize < size {return size } return round(size, _PageSize)}由于字符串abc的长度小于_MaxSmallSize(32768) , 故切片的长度只能取数组class_to_size中的值 ,即0, 8, 16, 32, 48, 64, 80, 96, 112, 128....s
至此, 现象一中切片容量为什么为8也真相大白了 。 相信到这里很多人已经明白现象四和现象五是怎么回事儿了, 其逻辑分别与现象一和现象二是一致的 ,有兴趣的 ,可以在自己的电脑上面试一试 。
字符串直接转切片那你说了这么多 ,现象三还是不能解释啊 。 请各位看官莫急 ,接下来我们继续分析 。
相信各位细心的小伙伴应该早就发现了我们在上面的cmd/compile/internal/gc/walk.go源码图中折叠了部分代码 ,现在我们就将这块神秘的代码赤裸裸的展示出来
推荐阅读
- 忆梦|电视接口都有哪些?各自有哪些作用?智能电视常见接口解析来了
- 忆梦|刘强东:电商没有制造就业,反而造成大量失业?真是这样吗?
- 忆梦|在国外火爆粒子漩涡壁纸来了,满满的科技感,赶紧来试试吧
- 忆梦|从iPhone11 Pro换成华为,憋了一肚心里话,不吐不快
- 忆梦|利用Sharding-JDBC解决数据库读写分离查询延时问题
- 忆梦|智云发布SMOOTH-XS手机云台,光是这颜色就爱了
- 忆梦|麒麟芯片成绝唱?华为手机全线涨价,最高涨 3000 元
- 忆梦|全县累计开通5G基站10个
- 忆梦|SpaceX和亚马逊正争夺参与定制FCC卫星规则的主导权
- 忆梦|称霸5G?做梦!被美国盯上重罚70亿,华为直接躺赢?
