Golang Go语言中 Slice 是如何扩容的?

发布于 1周前 作者 eggper 来自 Go语言

Golang Go语言中 Slice 是如何扩容的?

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


更多关于Golang Go语言中 Slice 是如何扩容的?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

回到顶部