CMS 并发标记与三色标记
候选人小庞在面试蚂蚁 P7 时,面试官问道:
"三色标记法是什么?为什么并发标记时可能会漏标对象?"
小庞说:"有白灰黑三种颜色..."面试官追问:"CMS 怎么解决漏标问题?"
小庞答不上来...
一、核心问题:三色标记与漏标 🔴
1.1 问题拆解
1.2 ❌ 错误示范
候选人原话 A:"并发标记时,三色标记保证不会漏标。"
问题诊断:三色标记本身不能保证并发正确性。需要额外的解决方案(增量更新或 SATB)。
候选人原话 B:"CMS 和 G1 用的是同一种方案。"
问题诊断:CMS 使用增量更新,G1 使用 SATB(Snapshot-At-The-Beginning)。
1.3 标准回答
P5 级别:三色定义
三色标记法(Tri-Color Marking):
P6 级别:漏标的原因
漏标的两个必要条件:
- 灰色对象到白色对象的引用被删除
- 灰色对象到白色对象之间插入了新的引用(或其他灰色对象指向了该白色对象)
具体场景:
CMS 的解决方案:增量更新(Incremental Update):
当一个黑色对象新增指向白色对象的引用时,将这个引用记录下来。在重新标记阶段,重新扫描这些被记录的黑色对象。
P7 级别:SATB vs 增量更新
CMS:增量更新(Incremental Update):
- 记录黑色 → 白色的新增引用
- 重新标记时,从这些黑色对象重新出发扫描
- 优点:更保守(不会漏标)
- 缺点:需要额外扫描(可能多扫描已确认存活的对象)
G1:SATB(Snapshot-At-The-Beginning):
SATB 在并发标记开始时创建堆内存的快照,所有在快照中存在的引用关系都被视为"有效":
- 在并发标记期间,灰色 → 白色的引用被删除时,将这个关系记录
- 删除的白色对象被视为"在快照中存活"
- 重新标记时,扫描这些被记录的白色对象
SATB 的特点:
- 更快的重新标记(只需要处理被删除的引用)
- 更保守(可能产生浮动垃圾,因为快照中存在的引用被保留)
【面试官心理】 这道题我能问到 P7 级别,是因为 SATB vs 增量更新是现代 GC 设计的核心技术差异。能说清两者原理的候选人说明他理解了并发 GC 的理论挑战。
1.4 追问升级
追问:为什么 G1 选择 SATB 而不是增量更新?
G1 使用 SATB 是因为 SATB 的重新标记阶段更快(只需扫描被删除的引用)。G1 的目标是可预测的停顿时间,SATB 更适合这个目标。但代价是会产生更多浮动垃圾。
二、生产避坑 🟢
2.1 并发标记期间的写屏障开销
2.2 浮动垃圾的影响
CMS 和 G1 都会产生浮动垃圾。浮动垃圾在下次 GC 时被回收,不会造成内存泄漏,但会增加 GC 频率。
面试加分点:能说出"ZGC 使用读屏障(Read Barrier)而非写屏障(Write Barrier),通过指针着色技术避免了写屏障的性能开销——读屏障在每次读取对象引用时执行,开销更低(通过编译器优化可以实现极低开销)",说明他理解了 ZGC 的设计哲学。
面试陷阱:被问到"SATB 的'Snapshot'是什么时候拍的",很多人会说"并发标记开始时"。准确答案是:SATB 在并发标记开始时创建快照,记录所有在快照中存在的引用关系。这意味着在并发标记期间被 GC 标记为不可达的对象,只要在快照中是可达的,就被视为存活。