Go 是一种垃圾收集语言:
http://golang.org/doc/go_faq.html#garbage_collection
这里它说它是一个标记和清除垃圾收集器,但它没有深入研究细节,并且正在开发替代品......然而,自从 Go 发布以来,这一段似乎没有太多更新。
它仍然是标记和扫描?它是保守的还是精确的?是世代相传的吗?
Go 1.4+ 垃圾收集器的计划:
混合停止世界/并发收集器
停止世界的部分有 10 毫秒的最后期限
专用于运行并发收集器的 CPU 内核
三色标记和扫描算法
非代际
非压实
完全精确
如果程序正在移动指针,则会产生少量成本
与 Go 1.3 GC 相比,延迟更低,但吞吐量也很可能更低
Go 1.3 垃圾收集器在 Go 1.1 之上的更新:
并发扫描(导致更小的暂停时间)
完全精确
Go 1.1 垃圾收集器:
标记和扫描(并行实现)
非代际
非压实
大部分是精确的(堆栈帧除外)
停止世界
基于位图的表示
程序不分配内存时的零成本(即:四处改组指针与 C 中一样快,尽管实际上这比 C 运行得慢一些,因为 Go 编译器不如 GCC 等 C 编译器先进)
支持对象的终结器
不支持弱引用
Go 1.0 垃圾收集器:
与 Go 1.1 相同,但垃圾收集器不是最精确的,而是保守的。保守的 GC 能够忽略诸如 []byte 之类的对象。
用不同的 GC 替换 GC 是有争议的,例如:
除了非常大的堆外,尚不清楚分代 GC 总体上是否会更快
包“不安全”使得难以实现完全精确的 GC 和压缩 GC
(对于 Go 1.8 - Q1 2017, see below)
下一个 Go 1.5 并发垃圾收集器涉及能够“调整”所说的 gc。
这是一个提出的建议in this paper,它可能适用于 Go 1.5,但也有助于理解 gc去。
可以看到1.5之前的状态(Stop The World: STW)
在 Go 1.5 之前,Go 使用了并行停止世界 (STW) 收集器。虽然 STW 收集有许多缺点,但它至少具有可预测和可控的堆增长行为。
https://i.stack.imgur.com/yLsPD.jpg
(来自 GopherCon 2015 演示文稿“Go GC: Solving the Latency Problem in Go 1.5”的照片)
STW 收集器的唯一调整旋钮是“GOGC”,即收集之间的相对堆增长。默认设置 100% 会在每次堆大小比上一次收集时的活动堆大小翻倍时触发垃圾收集:
https://i.stack.imgur.com/QFIS6.png
STW 收集器中的 GC 计时。
Go 1.5 引入了并发收集器。与 STW 收集相比,这有很多优点,但它使堆增长更难控制,因为应用程序可以在垃圾收集器运行时分配内存。
https://i.stack.imgur.com/rVa2V.jpg
(来自 GopherCon 2015 演示文稿“Go GC: Solving the Latency Problem in Go 1.5”的照片)
为了达到相同的堆增长限制,运行时必须更早开始垃圾收集,但提前多长时间取决于许多变量,其中许多变量无法预测。过早启动收集器,应用程序会执行过多的垃圾收集,浪费 CPU 资源。太晚启动收集器,应用程序将超过所需的最大堆增长。在不牺牲并发性的情况下实现正确的平衡需要仔细调整垃圾收集器的步调。 GC pacing 旨在从两个维度进行优化:堆增长和垃圾收集器使用的 CPU。
https://i.stack.imgur.com/s3sa8.png
GC pacing 的设计由四个部分组成:一个 GC 周期所需扫描工作量的估计器,一个在堆分配达到堆目标时执行估计扫描工作量的机制,一个后台调度器当 mutator 辅助未充分利用 CPU 预算时进行扫描,以及用于 GC 触发器的比例控制器。该设计平衡了两种不同的时间视图:CPU 时间和堆时间。 CPU 时间类似于标准挂钟时间,但比 GOMAXPROCS 快了几倍。也就是说,如果 GOMAXPROCS 为 8,则每墙秒通过 8 秒 CPU 时间,GC 每墙秒获得 2 秒 CPU 时间。 CPU 调度程序管理 CPU 时间。堆时间的流逝以字节为单位,并随着 mutators 分配而向前移动。堆时间和墙时间之间的关系取决于分配率,并且可以不断变化。 Mutator 协助管理堆时间的流逝,确保在堆达到目标大小时已完成估计的扫描工作。最后,触发器控制器创建一个反馈循环,将这两种时间视图联系在一起,针对堆时间和 CPU 时间目标进行优化。
这是GC的实现:
https://github.com/golang/go/blob/master/src/runtime/mgc.go
从源中的文档:
GC 与 mutator 线程并发运行,类型准确(又名精确),允许多个 GC 线程并行运行。它是一个使用写屏障的并发标记和扫描。它是非分代和非压缩的。分配是使用每个 P 分配区域隔离的大小来完成的,以最大程度地减少碎片,同时消除常见情况下的锁定。
Go 1.8 GC 可能会再次发展,proposal "Eliminate STW stack re-scanning"
从 Go 1.7 开始,剩下的一个无限且可能不平凡的停止世界 (STW) 时间的来源是堆栈重新扫描。我们建议通过切换到混合写入屏障来消除堆栈重新扫描的需要,该混合写入屏障结合了 Yuasa 样式的删除写入屏障 [Yuasa '90] 和 Dijkstra 样式的插入写入屏障 [Dijkstra '78]。初步实验表明,这可以将最坏情况的 STW 时间减少到 50µs 以下,并且这种方法可以使完全消除 STW 标记终止变得切实可行。
announcement is here,您可以看到相关的源提交是 d70b0fe 和更早。
我不确定,但我认为当前的(提示)GC 已经是并行的,或者至少它是 WIP。因此,stop-the-world 属性不再适用或在不久的将来不会适用。也许其他人可以更详细地澄清这一点。
不定期副业成功案例分享