1.18版本后的Slice扩容
新版公式: newcap += (newcap + 3*threshold) / 4
,threshold
默认为256
1.18 之前(老算法)
无论容量多大,一律 按 2 倍 往上翻。
结果:- 小切片(<128 B)没问题,几次就够用;
- 大切片(>1 MB)也 2 倍涨,第一次 1M→2M,第二次 2M→4M……
内存浪费明显,而且越往后每次浪费越夸张。
1.18 之后(新算法)
目标:小切片涨得快,大切片涨得慢,减少内存浪费。
于是引入一个 threshold = 256 的“阈值”,把扩容策略拆成三段:
- 期望容量 >
2×旧容量
用户一次性 append 很多,直接“一步到位”用期望容量,避免多次扩容。 - 旧容量 <
256
(小切片)(256
是元素个数)
仍然保持 2 倍,让小切片快速长大,减少扩容次数。 旧容量 ≥
256
(大切片)
不再 2 倍,而是改为 “平滑因子”增长,核心代码就是:newcap += (newcap + 3*threshold) / 4
把 threshold=256 代入:
newcap += (newcap + 768) / 4
等价于
newcap = newcap * 1.25 + 192 // 近似 1.25 倍
也就是说:
- 增长系数从 2.0 降到 约 1.25;
- 随着 newcap 越来越大,常数项 192 占比越来越小,实际系数趋近于 1.25。
这样 1 MB 的切片下一次只涨到 1.25 MB 左右,而不是 2 MB,内存压力大幅缓解。
总结
那行代码newcap += (newcap + 3*threshold) / 4
就是 Go1.18 给“大切片”设计的 1.25 倍缓慢增长公式,
用“加法式”而不是“乘法”实现,既避免浮点,又让涨幅从 2 倍平滑降到 1.25 倍左右,兼顾 CPU 和内存效率。
评论(0)