Go 语言切片是如何扩容的?( 三 )

 和之前版本的区别 , 主要在扩容阈值 , 以及这行代码:newcap += (newcap + 3*threshold) / 4 。
在分配内存空间之前需要先确定新的切片容量 , 运行时根据切片的当前容量选择不同的策略进行扩容:

  1. 如果期望容量大于当前容量的两倍就会使用期望容量;
  2. 如果当前切片的长度小于阈值(默认 256)就会将容量翻倍;
  3. 如果当前切片的长度大于等于阈值(默认 256) , 就会每次增加 25% 的容量 , 基准是 newcap + 3*threshold , 直到新容量大于期望容量;
内存对齐分析完两个版本的扩容策略之后 , 再看前面的那段测试代码 , 就会发现扩容之后的容量并不是严格按照这个策略的 。
那是为什么呢?
实际上 , growslice? 的后半部分还有更进一步的优化(内存对齐等) , 靠的是 roundupsize? 函数 , 在计算完 newcap 值之后 , 还会有一个步骤计算最终的容量:
capmem = roundupsize(uintptr(newcap) * ptrSize)newcap = int(capmem / ptrSize) 
这个函数的实现就不在这里深入了 , 先挖一个坑 , 以后再来补上 。
总结切片扩容通常是在进行切片的 append? 操作时触发的 。在进行 append? 操作时 , 如果切片容量不足以容纳新的元素 , 就需要对切片进行扩容 , 此时就会调用 growslice 函数进行扩容 。
切片扩容分两个阶段 , 分为 go1.18 之前和之后:
一、go1.18 之前:
  1. 如果期望容量大于当前容量的两倍就会使用期望容量;
  2. 如果当前切片的长度小于 1024 就会将容量翻倍;
  3. 如果当前切片的长度大于 1024 就会每次增加 25% 的容量 , 直到新容量大于期望容量;
二、go1.18 之后:
  1. 如果期望容量大于当前容量的两倍就会使用期望容量;
  2. 如果当前切片的长度小于阈值(默认 256)就会将容量翻倍;
  3. 如果当前切片的长度大于等于阈值(默认 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/




推荐阅读