以前是从别人的博客看到关于 slice 的扩容机制,有人问起来就搬别人博客上看到的内容回答,回答的时候总是不够自信,今天通过学习源码和借助 dlv 工具,算是明白了 slice 扩容的事情,这里记录下自己总结的结论,slice 源码也不难,有想深入了解的伙伴可以看 src/runtime/slice.go 中的 growslice 方法。
要是有理解错误的地方,希望能大佬指正。
我源码阅读的是 Go 1.14.6 版本
这里以 old = append(old, append...) 为例子说明
1 触发扩容的条件: 如果 old.len + append.len > old.cap 就会触发扩容
2: 扩容的时候调用的 src/runtime.growslice growslice(et *_type, old slice, cap int) slice
方法,该方法接收 slice 的元素类型(el)、old slice 以及预期容量长度 cap, 预期 cap: expectCap = old.len + append.len
3: growslice 的方法会预先获取到新的容量;再根据 slice 元素类型(size) 做内存对齐,最终计算出新的 cap 值
3.1: 预先获取新 newCap 的方式如下: 如果 expectCap > 2 * old.cap, 则 newCap = expectCap ; 否则,如果 old.cap < 1024, newCap = 2 * old.cap, 否则以 old.cap 的长度作为 newCap 初始值,以的 0.25 的步长循环增长,直到 newCap >= expectCap
3.2: 内存对齐,根据 newCap * slice 元素 size 获取内存空间,根据 golang 内置的一个内存表向上去整,拿到做内存对其后的内存空间 capmem,然后用 capmem 除以 slice 元素类型 size 得到最终的 newCap
看到网上很多人写的 slice 的扩容机制,其实只是写到了 3.1 这步 (也可能是因为和我看的 Go 版本不同),我做实验验证也发现有时候出现和文章描述不符合的结果,今天看了源码才终于明白,出现不符合的情况就是因为有 3.2 的情况出现
1
wewin OP 欢迎大佬评论和指正
|
2
wellsc 2021-05-23 16:25:30 +08:00 via iPhone 13
好好的周末整什么八股文
|
3
wewin OP 收藏的不少,感谢的比较少,收不到感谢后面是不是没有金币发文章了 😢
|