文章目录
- [一、三色标记中的漏标来源(CMS 与 G1 都要解决这个)](#一、三色标记中的漏标来源(CMS 与 G1 都要解决这个))
- [二、CMS vs G1 写屏障:核心对比表](#二、CMS vs G1 写屏障:核心对比表)
- 三、动画级理解
-
- [情况 ①:黑对象新增指向白对象](#情况 ①:黑对象新增指向白对象)
- [情况 ②:灰对象删除对另一个对象的引用(危险:对象消失)](#情况 ②:灰对象删除对另一个对象的引用(危险:对象消失))
- 四、为什么拦的时机不一样但都能正确?
- 五、总结
CMS 拦"新增引用"
G1 拦"删除引用"
为什么拦的东西不一样?为什么都能避免漏标?
一、三色标记中的漏标来源(CMS 与 G1 都要解决这个)
漏标的根本原因:
并发标记期间,应用线程改变引用结构 → 让 GC 的视图与真实情况不一致。
有两类危险变化:
| 场景 | 例子 | 危险 |
|---|---|---|
| ① 黑对象新增指向白对象 | A(黑).ref = C(白) |
C 永不再被扫描 → 被误删 |
| ② 灰对象删除对白对象的引用 | A(灰).ref = null |
B 被"从可达图消失" → 被误删 |
CMS 处理第一类
G1(SATB)处理第二类
下面正式进入对比。
二、CMS vs G1 写屏障:核心对比表
| 项目 | CMS(Incremental Update) | G1(SATB) |
|---|---|---|
| 写屏障触发时机 | 新增引用时触发 A → B | 删除引用时触发 A → B(旧引用保留) |
| 拦截内容 | 新的引用指向 | 被删掉的旧引用 |
| 防止的漏标类型 | 黑对象新增指向白对象 | 灰对象丢失引用导致对象消失 |
| 保证的视图 | 标记阶段的实时可达性 | GC 开始那一刻的快照可达性 |
| 原理 | 把新增引用的目标对象重新标记为灰色 | 把旧引用对象加入"快照缓冲区",继续标记 |
| 哲学 | "新增引用不能漏" | "起始快照不能漏" |
| 更复杂程度 | 较简单 | 更复杂(但更强健) |
三、动画级理解
我们用一个统一的例子比较:
A → B → C
GC 扫描到一半:
A(黑) → B(灰) → C(白)
现在应用线程做事了。
情况 ①:黑对象新增指向白对象
应用线程执行:
java
A.ref2 = C
形成:
A(黑) ─▶ C(白) (危险:黑 → 白)
🔶 CMS 做法(Incremental Update 写屏障)
拦新增引用:
"黑对象 A 指向了白对象 C?
C 必须重新标记为灰色!"
动画:
A(黑) → C(灰)
这样 C 就会被标记,不会漏。
🔷 G1(SATB)怎么处理这个?
G1 不处理!直接无视!
为什么?
因为 SATB 的视角是:
"我只保证 GC 开始时 的存活快照正确。
C 在开始时本来就是白色(垃圾)。
你后面引用它不影响这次 GC 的正确性。"
结果:
- CMS 认为 C 不应被删除,必须标记
- G1 认为 C 本来就可回收,标不标都无所谓
🎯 这是两者哲学的核心区别!
情况 ②:灰对象删除对另一个对象的引用(危险:对象消失)
应用线程执行:
java
B.ref = null
原本:
B(灰) → C(白)
变成:
B(灰) C(白)
C 成为"孤儿",危险。
🔷 G1(SATB)做法
拦删除引用:
- 写屏障记录旧引用 B→C
- 把 C 放进 SATB Buffer → 继续标记
动画:
C(灰) → 最终变黑
SATB 守住的是:
"开始时 C 是可达的,我必须保证它被标记。"
🔶 CMS 会怎么做?
CMS 不处理删除引用!
CMS 的目标是避免黑→白(新增引用)
删除引用是否导致对象变成垃圾 → CMS 不关心
四、为什么拦的时机不一样但都能正确?
CMS 的世界观:
"黑对象新增引用白对象最危险,我要补标。"
CMS 的 GC 模型:
- 保证"实时可达性"
- 所以新增引用必须补标
- 删除引用不会造成错误(对象从可达变不可达没关系)
G1(SATB)的世界观:
"我要保证 GC 开始那一瞬间的快照正确。"
所以:
- 删除引用 → 可能导致当时的存活对象丢失 → 必须拦
- 新增引用 → 不在快照里,不需要补标
🎯 G1 目标不是实时可达性,而是快照一致性。
五、总结
场景 1:黑新增指向白
A(黑) ─▶ C(白)
- CMS:把 C 变灰(补标)
- G1:忽略(C 不属于起始快照,不需要处理)
场景 2:灰丢失引用白
B(灰) → C(白) 删除引用后 → C 消失
- CMS:不处理
- G1:把旧 C 记录 → 变灰 → 标黑
CMS 写屏障采用 Incremental Update,拦"新增引用",修补黑→白问题。
G1 写屏障采用 SATB,拦"删除引用",保存旧引用,保护起始快照中的对象。
一个维护"实时可达性",一个维护"快照一致性"。