Go使用什么样的垃圾收集?

Go是一种垃圾收集语言:

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

这里说它是一个标记和清理垃圾收集器,但它并没有深入细节,并且正在进行replace……但是,自从Go发布以来,这个段落看起来并没有被更新。

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

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

  • 混合停止世界/并发收集器
  • 停止世界的一部分限制10毫秒的截止date
  • 专用于运行并发收集器的CPU内核
  • 三色标记和扫描algorithm
  • 非代
  • 非压实
  • 完全精确
  • 如果程序正在移动指针,则会产生小的成本
  • 与Go 1.3 GC相比,延迟更低,但最有可能的吞吐量也更低

Go 1.1上的Go 1.3垃圾回收器更新:

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

去1.1垃圾收集器:

  • 标记和扫描(并行实现)
  • 非代
  • 非压实
  • 大多数是精确的(堆栈帧除外)
  • 停止这世界
  • 基于位图的表示
  • 当程序没有分配内存时,它是零成本的(也就是说:在C语言中混洗指针的速度和C语言一样快,但实际上这比C慢,因为Go编译器不像C编译器那样先进,比如GCC)
  • 支持对象的终结器
  • 没有支持弱引用

去1.0垃圾收集器:

  • 和Go 1.1一样,但是垃圾收集器并不是很精确,而是保守的。 保守的GC可以忽略诸如[]字节的对象。

用另一种replaceGC是有争议的,例如:

  • 除非是非常大的堆,否则一代GC是否会更快
  • 封装“不安全”使得难以实现完全精确的GC和压缩GC

( 前往1.8 – 2017年第一季,见下文 )

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

你可以看到1.5 之前的状态(StopWorld:STW)

在Go 1.5之前,Go使用了一个平行世界 (STW)收集器。
虽然STW收集有许多缺点,但它至less具有可预测和可控的堆增长行为。

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

GC时间在STW收集器。

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

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

(图片来自GopherCon 2015演示文稿“ Go GC:解决Go 1.5中的延迟问题 ”)

为了达到同样的堆增长限制,运行时必须尽早开始垃圾收集,但是早期多less取决于许多variables,其中许多variables是无法预测的。

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

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

GC起搏的目标是在两个方面进行优化:堆积增长和垃圾收集器所使用的CPU。

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

GC起搏器的devise由四个部分组成:

  1. GC循环将需要的扫描工作量的估计器,
  2. 增量分配到达堆目标时,增变器执行估计的扫描工作量的机制,
  3. 一个调度程序的后台扫描,当变异者协助利用不足的CPU预算,和
  4. GC触发器的比例控制器。

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

  • CPU时间就像标准的挂钟时间,但通过GOMAXPROCS时间更快。
    也就是说,如果GOMAXPROCS是8,那么8个CPU秒就通过每一个墙壁,而GC则是每隔一个墙壁秒钟得到2秒的CPU时间。
    CPU调度程序pipe理CPU时间。
  • 堆时间的传递以字节为单位进行测量,并随着增变器的分配而向前移动。

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

这是GC的实施:

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

从源文件:

GC与mutator线程同时运行,types准确(又精确),允许多个GC线程并行运行。 这是一个并发的标记和扫描,使用写屏障。 这是非世代和不紧缩。 分配是通过使用每个P分配区域隔离的大小来完成的,以尽量减less碎片,同时消除常见情况下的locking。

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

从Go 1.7开始,剩下的一个无限的和潜在的非平凡世界(STW)时间的来源是堆栈重新扫描。

我们build议通过切换到结合Yuasa样式的删除写障碍(Yuasa '90)和Dijkstra风格的插入写障碍(Dijkstra '78)的混合写障碍来消除堆栈重新扫描的需要。

初步实验表明,这可以将最坏情况下的STW时间减less到50μs以下 ,并且这种方法可以使得完全消除STW标记终止是可行的。

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

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