Phil Eaton 深入观察了 Go 1.26 中将 Green Tea GC 设为默认垃圾回收器后的实际表现。通过 perf 工具和堆可视化技术,文章直观展示了 Go 如何按大小类别(size class)在连续的 8KiB 页面(span)中分配对象,并详细对比了新旧 GC 的缓存友好性差异。文章还触及了 Go 作为非移动式回收器(non-moving collector)的固有问题——无法回收稀疏页面(sparse pages),这在某些场景下会导致显著的内存浪费。作者同时对比了 Go 和 C# 的堆布局差异,为系统性能优化提供了宝贵的参考视角。
🔑 核心要点
Go 1.26 的 Green Tea GC 成为默认选项,相比旧版在缓存友好性上有显著提升,尤其利好特定类型的程序。
Go 的分配器源自 tcmalloc,按大小类别将对象分组到连续的 span 中,一个 span 包含一个或多个 8KiB 页面。
使用 perf 工具观察 GC 行为:通过堆地址遍历和对象地址分布可视化,直观呈现内存布局。
Go 作为非移动式 GC 的最大痛点是不能压缩内存,导致稀疏页面无法被回收,造成内存碎片化。
对比 C# 的堆布局展示了移动式 GC 在内存压缩方面的优势,但也带来了额外的写屏障开销。
文章附带完整的可复现代码和堆可视化输出,是理解 Go 内存管理机制的实战级参考资料。
💡 金句
Go's non-moving collector cannot reclaim sparse pages. This is the residual garbage collector bugaboo. ——Go 的非移动式回收器无法回收稀疏页面,这是其 GC 的顽固痛点。