GC 并发三色标记法(Concurrent Tri-color Marking)
并发三色标记法(Concurrent Tri-color Marking)是 Go 语言为了实现高性能、低延迟的内存管理而采用的核心垃圾回收算法。
简单来说,它将堆上的对象根据“是否被扫描过”分为三种颜色,并在 用户程序运行的同时 并发地进行标记。
1. 三种颜色的定义
三色标记法本质上是一种抽象概念,用来描述对象在 GC 过程中的状态:
-
白色 (White):潜在的垃圾对象。在 GC 开始时,所有对象都是白色的。如果标记结束时对象仍为白色,说明它不可达,其内存将被释放。
-
灰色 (Grey):中间状态。对象已被回收器访问,但回收器还需要进一步扫描它引用的子对象。
-
黑色 (Black):存活对象。对象已被访问且其所有引用都已扫描完毕。黑色对象绝不会直接指向白色对象。
2. 标记的核心流程
GC 并不是瞬间完成的,而是通过以下步骤循环进行:
-
初始状态:所有堆对象均为白色。
-
扫描根对象:从“根集合”(如全局变量、栈上的局部变量、寄存器)开始,将直接引用的对象标记为灰色。
-
处理灰色对象:从灰色集合中取出一个对象,将其引用的所有白色对象标记为灰色,然后将该对象自身标记为黑色。
-
重复执行:不断重复第 3 步,直到灰色集合为空。
-
清理白色:此时剩下的所有白色对象都是不可达的垃圾,直接回收其内存。
3. 为什么要“并发”?
传统的“标记-清除”算法需要 STW (Stop The World),即暂停所有用户代码,等 GC 完事了再恢复。这会导致明显的程序卡顿。
Go 的并发三色标记法允许 GC 线程与用户逻辑同时运行。但为了防止并发操作导致“误删”存活对象,Go 引入了写屏障(Write Barrier)技术:
- 混合写屏障:在 GC 期间,如果用户代码修改了对象引用,写屏障会强行将相关对象标记为灰色或黑色,从而确保原本存活的对象不会在扫描过程中被意外当成垃圾。
4. 总结
并发三色标记法的核心优势在于 极短的停顿时间。通过将标记过程拆分并与用户代码并发执行,Go 能够在大规模堆内存的情况下,依然保持毫秒甚至微秒级的 GC 延迟。
注意:虽然这种算法大幅提升了响应速度,但也会消耗一定的 CPU 资源用于“边扫边标”,并且由于写屏障的存在,回收精度会稍微下降(即本轮 GC 可能会遗留一些“浮动垃圾”到下一轮回收)。