GC 并发三色标记法(Concurrent Tri-color Marking)

并发三色标记法(Concurrent Tri-color Marking)是 Go 语言为了实现高性能、低延迟的内存管理而采用的核心垃圾回收算法。

简单来说,它将堆上的对象根据“是否被扫描过”分为三种颜色,并在 用户程序运行的同时 并发地进行标记。


1. 三种颜色的定义

三色标记法本质上是一种抽象概念,用来描述对象在 GC 过程中的状态:

  • 白色 (White)潜在的垃圾对象。在 GC 开始时,所有对象都是白色的。如果标记结束时对象仍为白色,说明它不可达,其内存将被释放。

  • 灰色 (Grey)中间状态。对象已被回收器访问,但回收器还需要进一步扫描它引用的子对象。

  • 黑色 (Black)存活对象。对象已被访问且其所有引用都已扫描完毕。黑色对象绝不会直接指向白色对象。


2. 标记的核心流程

GC 并不是瞬间完成的,而是通过以下步骤循环进行:

  1. 初始状态:所有堆对象均为白色。

  2. 扫描根对象:从“根集合”(如全局变量、栈上的局部变量、寄存器)开始,将直接引用的对象标记为灰色

  3. 处理灰色对象:从灰色集合中取出一个对象,将其引用的所有白色对象标记为灰色,然后将该对象自身标记为黑色

  4. 重复执行:不断重复第 3 步,直到灰色集合为空。

  5. 清理白色:此时剩下的所有白色对象都是不可达的垃圾,直接回收其内存。


3. 为什么要“并发”?

传统的“标记-清除”算法需要 STW (Stop The World),即暂停所有用户代码,等 GC 完事了再恢复。这会导致明显的程序卡顿。

Go 的并发三色标记法允许 GC 线程与用户逻辑同时运行。但为了防止并发操作导致“误删”存活对象,Go 引入了写屏障(Write Barrier)技术:

  • 混合写屏障:在 GC 期间,如果用户代码修改了对象引用,写屏障会强行将相关对象标记为灰色或黑色,从而确保原本存活的对象不会在扫描过程中被意外当成垃圾。

4. 总结

并发三色标记法的核心优势在于 极短的停顿时间。通过将标记过程拆分并与用户代码并发执行,Go 能够在大规模堆内存的情况下,依然保持毫秒甚至微秒级的 GC 延迟。

注意:虽然这种算法大幅提升了响应速度,但也会消耗一定的 CPU 资源用于“边扫边标”,并且由于写屏障的存在,回收精度会稍微下降(即本轮 GC 可能会遗留一些“浮动垃圾”到下一轮回收)。