[面试题] 什么是三色标记算法?为什么初始标记和重新标记需要 STW?
📖 版块 A:知识讲解版 (温故知新)
-
三色标记算法:
- 白色:尚未被访问到的对象,默认是垃圾候选。
- 灰色:已经被发现,但其引用出去的对象还没有全部扫描完。
- 黑色:对象本身以及它引用出去的对象都已经扫描完。
-
并发标记的问题:
- 如果用户线程和 GC 线程同时修改对象引用关系,就可能破坏标记过程。
- 这会导致漏标或误标。
-
为什么初始标记需要 STW:
- 需要快速从 GC Roots 建立初始可达对象集合。
- 这个起点必须尽量一致可靠。
-
为什么重新标记需要 STW:
- 并发标记期间,用户线程仍然在修改引用关系。
- 重新标记要把这期间遗漏的变化补回来,修正最终结果。
-
一句话理解:
- 并发标记可以并发做大部分脏活,但在“确定起点”和“最终收口”这两个阶段,仍然需要 STW 保证一致性。
🎤 版块 B:面试实战版 (高手逐字稿)
Q:什么是三色标记算法?为什么初始标记和重新标记需要 STW?
💬 高手逐字稿:
三色标记算法,本质上是垃圾回收器在并发标记时,用来管理对象扫描状态的一种抽象模型。
它会把对象分成三类:
- 白色,表示还没访问到,默认可能是垃圾;
- 灰色,表示这个对象已经发现了,但它引用出去的对象还没扫完;
- 黑色,表示这个对象和它能到达的引用都已经处理完了。
如果整个程序是静止的,这套模型很好用。但问题在于,现代回收器很多时候是并发标记,也就是 GC 标记时,用户线程还在继续运行,还在不断修改对象之间的引用关系。
这就会带来一个核心风险:GC 看到的对象图在变化。 一旦变化时机不巧,就可能出现本来还活着的对象没有被正确标记,也就是漏标。
所以为什么初始标记和重新标记必须 STW?
因为 初始标记 要快速确定从 GC Roots 出发的起点集合,这一步如果不停顿,根集合本身就在漂移,后面整条标记链都会不可靠。
而 重新标记 则是为了在并发标记结束之后,修正用户线程在并发阶段造成的引用变化。你可以把它理解成最后一次“对账”,确保没有漏掉重要改动。
所以并发回收的正确理解应该是:它不是完全不要 STW,而是把大部分耗时工作并发掉,只在最关键的起点和收尾阶段保留短暂 STW。
最后更新于: