和之前版本的区别 , 主要在扩容阈值 , 以及这行代码:newcap += (newcap + 3*threshold) / 4 。
在分配内存空间之前需要先确定新的切片容量 , 运行时根据切片的当前容量选择不同的策略进行扩容:
- 如果期望容量大于当前容量的两倍就会使用期望容量;
- 如果当前切片的长度小于阈值(默认 256)就会将容量翻倍;
- 如果当前切片的长度大于等于阈值(默认 256) , 就会每次增加 25% 的容量 , 基准是 newcap + 3*threshold , 直到新容量大于期望容量;
那是为什么呢?
实际上 , growslice? 的后半部分还有更进一步的优化(内存对齐等) , 靠的是 roundupsize? 函数 , 在计算完 newcap 值之后 , 还会有一个步骤计算最终的容量:
capmem = roundupsize(uintptr(newcap) * ptrSize)newcap = int(capmem / ptrSize)
这个函数的实现就不在这里深入了 , 先挖一个坑 , 以后再来补上 。
总结切片扩容通常是在进行切片的 append? 操作时触发的 。在进行 append? 操作时 , 如果切片容量不足以容纳新的元素 , 就需要对切片进行扩容 , 此时就会调用 growslice 函数进行扩容 。
切片扩容分两个阶段 , 分为 go1.18 之前和之后:
一、go1.18 之前:
- 如果期望容量大于当前容量的两倍就会使用期望容量;
- 如果当前切片的长度小于 1024 就会将容量翻倍;
- 如果当前切片的长度大于 1024 就会每次增加 25% 的容量 , 直到新容量大于期望容量;
- 如果期望容量大于当前容量的两倍就会使用期望容量;
- 如果当前切片的长度小于阈值(默认 256)就会将容量翻倍;
- 如果当前切片的长度大于等于阈值(默认 256) , 就会每次增加 25% 的容量 , 基准是 newcap + 3*threshold , 直到新容量大于期望容量;
参考文章:
- https://go.dev/doc/go1.18
- https://go.dev/blog/slices
- https://go.dev/blog/slices-intro
- https://golang.design/go-questions/slice/grow/
- https://draveness.me/golang/docs/part2-foundation/ch03-datastructure/golang-array-and-slice/
推荐阅读
- HTTPS是如何保证密文不能被篡改的?
- 让很多人直呼“要失业”,火爆网络的AIGC,到底是什么东西?
- “流动型程序员”指的是什么类型的程序员?
- 张继科|张继科欠不欠巨额赌债我不知道但张继科非常缺钱我倒是早就看出来
- 景甜|乱套了!那些买卖景甜视频的人,你们是疯了吗?
- |万能钓饵的正确用法,渔获差异巨大,原因是少了几个步骤
- 辅警|大量政府员工给辞退!主要是哪些岗位人员?
- 80年代|一盘棋总是有胜负的
- 薏仁红豆芡实水
- 清热消暑糖水