Go使用哪种垃圾收集?


111

Go是一种垃圾回收语言:

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

在这里,它说这是一个标志性的垃圾回收器,但是它没有深入研究细节,并且正在进行替代...但是,自Go发行以来,该段似乎没有太多更新。

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


2
有关Go垃圾收集器到2018年7月的历史的详细讨论,请参阅blog.golang.org/ismmkeynote
Wildcard

Answers:


117

Go 1.4+垃圾收集器的计划:

  • 混合世界/并行收集器
  • 截止日期限制为10毫秒
  • 专门用于运行并发收集器的CPU内核
  • 三色标记扫掠算法
  • 非世代
  • 不紧凑的
  • 完全精确
  • 如果程序在四处移动指针,则会产生少量费用
  • 与Go 1.3 GC相比,延迟更短,但很可能吞吐量也更低

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

  • 并发扫描(导致更短的暂停时间)
  • 完全精确

前往1.1垃圾收集器:

  • 标记扫描(并行实现)
  • 非世代
  • 不紧凑的
  • 最精确(堆叠框架除外)
  • 停止世界
  • 基于位图的表示
  • 程序不分配内存时的零成本(也就是说,改组指针的速度与C中一样快,尽管实际上这比C的运行速度慢一些,因为Go编译器不如GCC这样的C编译器先进)
  • 支持对象的终结器
  • 不支持弱引用

Go 1.0垃圾收集器:

  • 与Go 1.1相同,但垃圾回收器不是很精确,而是保守的。保守的GC可以忽略[] byte之类的对象。

用不同的GC替换GC是有争议的,例如:

  • 除了非常大的堆之外,尚不清楚世代GC总体上是否会更快
  • 软件包“不安全”使得难以实施完全精确的GC和压缩GC

另外,当前的垃圾收集器具有一定程度的并行性,因此可以在多核系统上更快地运行。
uriel

3
@uriel:是的,我在答案的第一项中提到了这一点-文本“(并行实现)”。

这个答案仍然是最新的吗?
Kim Stebel,2012年

C#垃圾收集器是精确的,在C#中像在go中一样,您可以引用被罢工的成员,而c#具有不安全模式,但是我不确定它与不安全实现的比较如何
skyde 2013年

3
用1.5.x更新此答案只是为了创建良好的历史记录。
伊斯梅尔

32

(对于Go 1.8-2017年第一季度,请参见下文

gc说,下一个Go 1.5 并发的垃圾收集器将涉及“步调”。
这是本文提出的建议中可能适用于Go 1.5,但也有助于理解Go中的gc。

您可以看到1.5 之前的状态(停止世界:STW)

在Go 1.5之前,Go使用了并行的世界停止(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收集相比,这具有许多优点,但它可以堆增长难以控制,因为应用程序可以在垃圾收集器运行时分配内存

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

GC起搏的设计包含四个组件:

  1. 一个GC周期所需的扫描工作量的估算器,
  2. 一种机制,用于使更改程序在堆分配达到堆目标时执行估计的扫描工作量,
  3. 当mutator协助未充分利用CPU预算时,用于后台扫描的调度程序;以及
  4. GC触发器的比例控制器。

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

  • CPU时间与标准挂钟时间相似,但是传递GOMAXPROCS时间更快。
    也就是说,如果GOMAXPROCS为8,则每墙每秒经过8个CPU秒,而GC每墙每秒获得2秒的CPU时间。
    CPU调度程序管理CPU时间。
  • 堆时间的流逝以字节为单位,并随着变量的分配而向前移动。

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


20

这是GC的实现:

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

从源代码中的文档中:

GC与mutator线程同时运行,类型准确(也称为精确),允许多个GC线程并行运行。它是使用写入屏障的并发标记和清除。它是非世代和非紧凑的。使用每个P分配区域隔离的大小来完成分配,以最大程度地减少碎片,同时消除常见情况下的锁定。


8

建议使用“消除STW堆栈重新扫描”建议,Go 1.8 GC可能会再次发展

从1.7版开始,堆栈重新扫描是无界且可能不重要的世界停止时间(STW)的唯一剩余来源。

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

初步实验表明,这可以将最坏情况下的 STW标志时间减少到50µs以下,并且这种方法可以使完全消除STW标志终止成为现实。

公告是在这里,你可以看到相关的源提交是d70b0fe和更早版本。


3

我不确定,但是我认为当前的(提示)GC已经是并行的,或者至少是WIP。因此,stop-the-world属性不再适用或在不久的将来不再适用。也许其他人可以更详细地说明这一点。


7
这是世界末日。在世界停止之后,GC可能并行运行。您可能意味着并发GC。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.