【问题标题】:What kind of Garbage Collection does Go use?Go 使用什么样的垃圾收集器?
【发布时间】:2011-12-11 01:03:25
【问题描述】:

Go 是一种垃圾收集语言:

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

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

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

【问题讨论】:

标签: garbage-collection go


【解决方案1】:

这是GC的实现:

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

来自源中的文档:

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

【讨论】:

    【解决方案2】:

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

    • 混合 stop-the-world/并发收集器
    • stop-the-world 部分的截止日期为 10 毫秒
    • 专用于运行并发收集器的 CPU 内核
    • 三色标记和扫描算法
    • 非代际
    • 非压缩
    • 完全精确
    • 如果程序四处移动指针会产生少量成本
    • 与 Go 1.3 GC 相比,延迟更低,但吞吐量也很可能更低

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

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

    Go 1.1 垃圾收集器:

    • mark-and-sweep(并行实现)
    • 非代际
    • 非压缩
    • 大部分精确(堆栈帧除外)
    • 停止世界
    • 基于位图的表示
    • 程序不分配内存时的零成本(即:混洗指针的速度与在 C 中一样快,尽管在实践中它的运行速度比 C 慢一些,因为 Go 编译器不如 GCC 等 C 编译器先进)
    • 支持对象的终结器
    • 不支持弱引用

    Go 1.0 垃圾收集器:

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

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

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

    【讨论】:

    • 另外目前的垃圾回收器具有一定的并行度,因此在多核系统上运行速度更快。
    • @uriel:是的,我在回答的第一项中提到了这一点 - 文本“(并行实现)”。
    • 这个答案仍然是最新的吗?
    • c# 垃圾收集器是精确的,在 c# 中,就像在 go 中一样,您可以引用被击中的成员,而 c# 具有不安全模式,但我不确定它与不安全实现相比如何
    • 用 1.5.x 更新这个答案怎么样,只是为了制作一个好的历史日志。
    【解决方案3】:

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

    【讨论】:

    • 这是世界末日。 GC 可能在世界停止后并行运行。您可能是指并发 GC。
    【解决方案4】:

    (对于Go 1.8 - Q1 2017, see below

    下一个 Go 1.5 并发垃圾收集器涉及能够“步调”所说的 gc。
    这是in this paper 提出的一个提案,它可能适用于 Go 1.5,但也有助于理解 Go 中的 gc。

    你可以看到之前 1.5(Stop The World: STW)的状态

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

    (照片来自GopherCon 2015演讲“Go GC: Solving the Latency Problem in Go 1.5”)

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

    STW 收集器中的 GC 计时。

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

    (图片来自GopherCon 2015演讲“Go GC: Solving the Latency Problem in Go 1.5”)

    要达到相同的堆增长限制,运行时必须更早开始垃圾回收,但提前多长时间取决于许多变量,其中许多变量无法预测。

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

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

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

    GC pacing 的设计由四个部分组成:

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

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

    • CPU 时间 类似于标准挂钟时间,但比标准时钟时间快GOMAXPROCS 倍。
      也就是说,如果GOMAXPROCS 为 8,则每壁秒通过 8 个 CPU 秒,而 GC 每壁秒获得 2 秒的 CPU 时间。
      CPU 调度程序管理 CPU 时间。
    • 堆时间的流逝以字节为单位,并随着 mutators 分配而向前移动。

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

    【讨论】:

      【解决方案5】:

      Go 1.8 GC 可能会再次进化,proposal "Eliminate STW stack re-scanning"

      从 Go 1.7 开始,剩余的一个无限且可能不平凡的 stop-the-world (STW) 时间来源是堆栈重新扫描。

      我们建议通过切换到结合了Yuasa-style deletion write barrier [Yuasa '90]Dijkstra-style insertion write barrier [Dijkstra '78] 的混合写入屏障来消除堆栈重新扫描的需要。

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

      announcement is here 可以看到相关的源提交是d70b0fe 及更早的版本。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-05-30
        • 1970-01-01
        • 2023-03-07
        • 2015-03-22
        • 2015-04-10
        • 2022-01-12
        • 1970-01-01
        相关资源
        最近更新 更多