写屏障通过在引用字段赋值处插桩,捕获并发修改,防止漏标:当灰色对象断开白色对象引用且黑色对象新增引用时,写屏障记录变更,触发重扫或重新标记。

写屏障在并发标记中的作用
写屏障不是用来“实现”并发标记的算法,而是保障并发标记正确性的关键机制。当 GC 线程和应用线程同时运行时,对象引用关系可能被程序线程快速修改,导致标记遗漏(漏标)。写屏障通过在引用字段赋值的**关键位置插入少量检测逻辑**,捕获这些变动,把可能被漏标的对象记录下来,供后续重新扫描或修正标记状态。
如何用写屏障拦截引用更新
JVM 在编译或解释执行过程中,对对象字段写操作(如 obj.field = new_obj)进行插桩。以最常用的**卡表(Card Table)+ 写屏障**为例:
- JVM 把堆划分为固定大小的卡页(通常 512B),每页对应卡表中一个字节
这种设计不阻塞应用线程,只增加极小的写开销(一次内存写 + 地址计算),却能有效控制重扫范围。
不同垃圾收集器的写屏障策略差异
并非所有收集器都用卡表:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- G1 使用更精细的 Remembered Set(RSet),写屏障在跨区域引用写入时,将源卡页信息加入目标 Region 的 RSet,避免全堆扫描
- ZGC / Shenandoah 采用 读屏障 + 指针染色 或 转发指针,写屏障主要用于更新引用时同步处理并发移动,标记本身依赖对象头位图和并发遍历
- CMS 曾使用增量更新(IU)或原始快照(SATB)写屏障:SATB 在引用被覆盖前记录旧值,保证“原本可达”的对象不会被回收
写屏障与标记算法的配合要点
写屏障本身不决定标记逻辑,它只为标记提供“变更快照”。实际并发标记流程依赖三色标记抽象:
- 白色:未访问、可回收
- 灰色:已访问、待扫描其引用
- 黑色:已访问、其引用也全部扫描完毕
写屏障主要防止从黑色对象新指向白色对象(破坏“黑→白不可达”假设)。例如 SATB 屏障会在 obj.field = white_obj 执行前,把 white_obj 压入标记栈,确保它最终被标记为灰色或黑色。
简单代码示意(伪代码)
这是 SATB 风格写屏障的简化逻辑:
void write_barrier_before(Object obj, ObjectField field, Object new_value) {
Object old_value = field.get(obj);
if (old_value != null && is_white(old_value)) {
push_to_mark_stack(old_value); // 记录旧引用,防止漏标
}
}
void write_barrier_after(Object obj, ObjectField field, Object new_value) {
// 可选:对 new_value 做快速标记或入队
}
真实 JVM 实现会做大量优化,比如内联、缓存局部栈、批量提交等,但核心思想一致:在引用关系变化的关键瞬间,留下痕迹。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










