在 Go 的垃圾回收(GC)机制中,三色标记法(Three-Color Marking) 是一种用于追踪内存中对象引用关系的算法。它将对象分为三种颜色,以描述它们在扫描过程中的状态:
三种颜色的含义
- 白色 (White):潜在的可回收对象。在 GC 开始时,所有对象都是白色的。如果在标记结束时对象仍然是白色,说明该对象不可达,会被回收。
- 灰色 (Grey):中间状态对象。该对象已被扫描器发现(被引用),但它内部引用的其他对象(子对象)还没有被扫描完。
- 黑色 (Black):活跃对象。该对象已被扫描器处理,且它内部引用的所有子对象也已经被标记为灰色或黑色。黑色对象绝对不会被回收,且扫描器不会再扫描它。
标记的基本流程
- 初始阶段:将所有对象标记为白色。
- 根扫描 (Root Scan):从根对象(Root objects,如全局变量、栈上的局部变量等)出发,将它们直接引用的对象标记为灰色并放入“灰色队列”。
- 并发标记 (Concurrent Mark):
- 从灰色队列中取出一个对象。
- 扫描该对象引用的所有子对象:
- 如果是白色,将其变为灰色并放入队列。
- 将当前对象标记为黑色。
- 重复步骤 3:直到灰色队列为空。
- 清理阶段:此时剩下的所有白色对象即为不可达对象,进行内存回收。
为什么在并发环境下需要它?
在并发标记过程中,如果业务逻辑(Mutator)修改了指针,可能会出现以下问题:
- 漏标:一个本来活跃的对象被误认为是白色,导致被错误回收。
三色标记法结合写屏障(Write Barrier) 就是为了解决这个问题。写屏障会确保在并发修改指针时,维持“黑色对象不能指向白色对象”或“灰色对象始终能追踪到存活对象”的约束(即强/弱三色不变性),从而保证 GC 的正确性。