V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
wewin
V2EX  ›  Go 编程语言

Go Slice 是如何扩容的 ?

  •  
  •   wewin · 2021-05-23 15:54:21 +08:00 · 2242 次点击
    这是一个创建于 1274 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    wewin
        1
    wewin  
    OP
       2021-05-23 16:01:30 +08:00
    欢迎大佬评论和指正
    wellsc
        2
    wellsc  
       2021-05-23 16:25:30 +08:00 via iPhone   ❤️ 13
    好好的周末整什么八股文
    wewin
        3
    wewin  
    OP
       2021-05-24 09:05:29 +08:00
    收藏的不少,感谢的比较少,收不到感谢后面是不是没有金币发文章了 😢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   911 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 22:27 · PVG 06:27 · LAX 14:27 · JFK 17:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.