5

Go Slice 是如何扩容的 ?

 3 years ago
source link: https://www.v2ex.com/t/778682
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

V2EX  ›  Go

Go Slice 是如何扩容的 ?

  wewin · 1 小时 0 分钟前 · 139 次点击

以前是从别人的博客看到关于 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 的情况出现


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK