Java垃圾回收中并发可达性分析依赖三色标记算法,读写屏障是防止漏标的核心机制;漏标发生在灰色对象断开白色对象唯一引用且黑色对象新增该引用时;G1/CMS用写屏障(SATB或增量更新),ZGC/Shenandoah用读屏障,均由JVM底层自动注入,不可手动调用。

Java 垃圾回收中,并发可达性分析依赖三色标记算法,而读写屏障是防止漏标的核心机制。漏标不是小概率事件,而是并发标记必然面临的风险——业务线程修改引用的同时,GC 线程正在推进标记,稍有不慎,一个本该存活的对象就会被当成垃圾回收,引发 NullPointerException 或数据错乱。屏障不靠魔法,靠的是在关键指令处“插桩”,精准拦截、记录、修正状态。
漏标发生的两个必要条件
理解屏障之前,先看清问题本身。漏标只在以下两个条件同时成立时发生:
- 灰色对象断开了对白色对象的唯一引用(比如
objE.fieldG = null) - 黑色对象新增了对该白色对象的引用(比如
objD.fieldG = G)
此时,GC 线程已跳过灰色对象 objE,又不会重新扫描黑色对象 objD,白色对象 G 就彻底“消失”在标记视野之外。
写屏障:G1/CMS 的主力防御手段
写屏障插入在引用写操作之后,本质是“补录变更”。它不阻止业务线程改引用,但确保所有可能造成漏标的写动作都被捕获。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- G1 使用 SATB(Snapshot-At-The-Beginning):在并发标记开始时拍下堆的“快照”,后续所有被删除的引用(如灰色→白色断开),都记入 SATB 缓冲区。标记结束后,以这些被删引用的目标为根,再做一次局部扫描
- CMS 使用 增量更新(Incremental Update):当黑色对象新增指向白色对象的引用时,写屏障立刻把该黑色对象“降级”回灰色,让它后续被重新扫描
- 两者都要求 JVM 在字节码层面识别
putfield、putstatic等写指令,并在对应机器码前后插入汇编级钩子
读屏障:ZGC/Shenandoah 的底层基石
读屏障插入在引用读操作之前,更激进也更通用。它不等漏标发生,而是在每次取引用时就做检查和转换。
- ZGC 在对象引用字段中存储的是“染色指针(colored pointer)”,读取前必须通过读屏障解析真实地址;若对象正在被转移,屏障自动完成重定向并更新引用
- 它天然覆盖漏标场景:即使灰色对象已断开引用,只要业务线程后续读到那个白色对象(比如从本地变量或栈中),读屏障就能触发标记或转发,避免对象丢失
- 代价是每次对象引用访问都有微小开销,但换来的是几乎无 STW 的并发转移与极低延迟
屏障不是 Java 代码,也不能手动调用
读写屏障是 JVM 在 JIT 编译或解释执行阶段,由 C++ 层(如 HotSpot 的 BarrierSet 子系统)自动注入的底层指令,比如 x86 上的 mfence、ARM 上的 dmb ish,或更轻量的内存访问控制逻辑。
- 开发者无法用
synchronized或volatile替代——那些是 JMM 层面的语义,解决线程间可见性;而 GC 屏障解决的是 GC 线程与业务线程对堆图结构认知的一致性 - 不同收集器启用不同屏障:启动 ZGC 会默认开启读屏障;G1 默认启用写屏障;CMS 同样依赖写屏障,但已废弃
- 可通过 JVM 参数验证,例如
-XX:+PrintGCDetails -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(需 hsdis)观察屏障指令生成

















