Go 使用什么类型的垃圾收集?

Go 是一种垃圾回收语言:

Http://golang.org/doc/go_faq.html#garbage_collection

这里说它是一个标记和清除垃圾收集器,但是它没有深入研究细节,并且正在进行替换... 然而,自从 Go 发布以来,这一段似乎没有更新多少。

它仍然是标记和扫描? 它是保守的还是精确的? 它是代?

48696 次浏览

这是 GC 的实现:

Https://github.com/golang/go/blob/master/src/runtime/mgc.go

来源:

GC 与 mutator 线程并发运行,类型准确(也称为精确) ,允许多个 GC 线程并行运行。它是使用写障碍的并发标记和扫描。它是非分代和非压缩的。分配是使用按 P 分配区域分隔的大小来完成的,这样可以最小化碎片,同时在通常情况下消除锁。

Go 1.4 + 垃圾收集器计划:

  • 停止世界/并发混合收集器
  • 在10毫秒的最后期限内停止世界部分
  • 专用于运行并发收集器的 CPU 内核
  • 三色标记扫描算法
  • 非世代的
  • 不压实
  • 非常精确
  • 如果程序在移动指针,则会产生很小的开销
  • 与 Go 1.3 GC 相比,延迟更低,但吞吐量也更低

Go 1.3垃圾收集器在 Go 1.1之上的更新:

  • concurrent sweep (results in smaller pause times)
  • 非常精确

Go 1.1垃圾收集器:

  • 标记和扫描(并行实现)
  • 非世代的
  • 不压实
  • 大部分精确(堆栈帧除外)
  • 让世界停止
  • 基于位图的表示法
  • 当程序不分配内存时,成本为零(也就是说: 移动指针的速度和 C 语言一样快,尽管实际上这比 C 语言运行得慢一些,因为 Go 编译器没有像 GCC 这样的 C 语言编译器那么高级)
  • 支持对象的终结器
  • 不支持弱引用

Go 1.0垃圾收集器:

  • 与 Go 1.1相同,但是垃圾收集器不是最精确的,而是比较保守的。保守的 GC 能够忽略诸如[] byte 之类的对象。

Replacing the GC with a different one is controversial, for example:

  • 除了非常大的堆之外,目前还不清楚一代 GC 总体上是否会更快
  • 包“不安全”使得很难实现完全精确的 GC 和紧凑的 GC

我不确定,但是我认为目前的 GC (技巧)已经是一个并行的 GC,或者至少是一个 WIP。因此,停止世界财产不再适用或不会在不久的将来。也许其他人可以更详细地澄清这一点。

(适用于 去1.8-Q12017,见下文)

The next Go 1.5 同时 Garbage Collector involve being able to "pace" said gc.
下面是一个提交给 在这张纸上的建议,它可能适用于 Go 1.5,但也有助于理解 Go 中的 gc。

您可以看到状态 before1.5(Stop The World: STW)

在 Go 1.5之前,Go 使用了 parallel stop-the-world(STW)收集器。
虽然 STW 集合有很多缺点,但它至少具有可预测和可控的堆增长行为。

https://40.media.tumblr.com/49e6556b94d75de1050c62539680fcf9/tumblr_inline_nr6qq8D9FE1sdck2n_540.jpg

(图片来自 GopherCon 2015讲座“ Go GC: 解决 Go 1.5中的延迟问题”)

STW 收集器的唯一调优旋钮是“ GOGC”,即收集之间的相对堆增长。默认设置为100% ,在堆大小比活动堆大小增加一倍时触发垃圾回收,如上一次回收时所示:

https://docs.google.com/drawings/image?id=sLJ_JvGfPfPnojLlEGLCWkw&rev=1&h=113&w=424&ac=1

STW 收集器中的 GC 计时。

Go 1.5引入 并发收集器并发收集器
这比 STW 集合有许多优点,但它是 m使堆增长更难控制,因为应用程序可以在垃圾收集器运行时分配内存

https://40.media.tumblr.com/783c6e557b427a5c023520578740eb94/tumblr_inline_nr6qqpmaJx1sdck2n_540.jpg

(图片来自 GopherCon 2015讲座“ Go GC: 解决 Go 1.5中的延迟问题”)

为了达到相同的堆增长限制,运行时必须更早地启动垃圾收集,但是提前多久取决于许多变量,其中许多变量无法预测。

  • 过早启动收集器,应用程序将执行太多垃圾收集,从而浪费 CPU 资源。
  • 启动收集器的时间太晚,应用程序将超过所需的最大堆增长。

要在不牺牲并发性的情况下实现正确的平衡,需要仔细调整垃圾收集器的速度。

GC 配速旨在沿着两个维度进行优化: 堆增长和垃圾收集器使用的 CPU。

https://docs.google.com/drawings/image?id=sEZYCf7Mc0E0EGmy4gho3_w&rev=1&h=235&w=457&ac=1

The design of GC pacing consists of four components:

  1. 一个 GC 周期所需扫描工作量的估计值,
  2. a mechanism for mutators to perform the estimated amount of scanning work by the time heap allocation reaches the heap goal,
  3. 当 mutator 协助未充分利用 CPU 预算时用于后台扫描的调度程序,以及
  4. GC 触发器的比例控制器。

设计平衡了 两种不同的时间观: CPU 时间和堆时间

  • CPU 时间 类似于标准的挂钟时间,但是通过 GOMAXPROCS的速度要快一倍。
    也就是说,如果 GOMAXPROCS为8,那么每一墙秒将传递8个 CPU 秒,而 GC 每一墙秒将获得2秒的 CPU 时间。
    CPU 调度程序管理 CPU 时间。
  • 一堆时间的传递是以字节为单位的,随着 mutator 的分配而向前移动。

堆时间和墙时间之间的关系取决于分配率,并且可以不断变化。
Mutator 协助管理堆时间的流逝,确保在堆达到目标大小时,估计的扫描工作已经完成。
最后,触发器控制器创建一个反馈回路,将这两个时间视图绑定在一起,从而优化堆时间和 CPU 时间目标。

去1.8 GC 可能再次进化,与 「消除重新扫描污水处理堆叠」建议书

到 Go 1.7为止,剩下的一个无限制且可能非平凡的 停止世界(STW)时间来源是堆栈重新扫描。

我们建议通过切换到结合 汤浅式删除写屏障[汤浅90]Dijkstra 风格的插入写屏障[ Dijkstra’78]的混合写屏障来消除堆栈重新扫描的需要。

Preliminary experiments show that this can 将最坏情况下的 STW 时间缩短至50μs 以下, and this approach may make it practical to eliminate STW mark termination altogether.

公告在这里中,您可以看到相关的源提交是 70bfe和更早的版本。