写屏障解决分代/分区回收中跨代引用漏标和并发标记漏标问题;它通过拦截引用更新,记录脏卡页或SATB日志,支撑G1等收集器高效定位跨区域引用并保障三色标记正确性。

写屏障是 JVM 垃圾回收器在对象引用发生变更时插入的一段底层拦截逻辑,不是 Java 代码,而是编译期或运行时注入到字节码或机器指令中的轻量钩子。它的核心任务有两个:一是协助 GC 高效定位跨区域引用(比如老年代指向新生代),二是保障并发标记阶段的对象存活状态不被漏标。
写屏障解决什么问题?
分代或分区回收(如 G1、ZGC)为了效率,不会每次 GC 都扫描整个堆。例如 Minor GC 只处理新生代,但若老年代某个对象正引用着新生代里的对象,这个引用就可能被忽略——导致本该存活的对象被错误回收。写屏障就是为堵住这个漏洞而设的实时监控机制。
- 避免全堆扫描:通过记录“谁改了谁的引用”,让 GC 只检查相关区域(如脏卡页)
- 防止并发漏标:在 GC 标记与用户线程并发执行时,拦截引用更新并修正三色标记状态
- 支撑区域化管理:G1 中每个 Region 的记忆集(Remembered Set)依赖写屏障持续更新
写屏障怎么工作?
它在每次对象字段赋值(a.field = b)这类写操作前后自动触发,但具体行为因 GC 算法而异:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- G1 使用 SATB(Snapshot-At-The-Beginning)日志写屏障:记录赋值前的旧引用(pre-value),供后续并发标记使用;不立即更新数据结构,而是写入线程本地 SATB 队列,由后台线程批量处理
- CMS 和部分老式收集器用增量更新式写屏障:直接修改卡表(Card Table)标志位,把对应卡页标为“脏”,确保下次 GC 能扫描到
- ZGC 和 Shenandoah 不依赖写屏障做跨代记录,而用读屏障+彩色指针:它们把引用有效性判断前移到读操作,写屏障在这里退居次要角色
写屏障的开销与优化
它本身极轻量,但高频调用仍会带来一定性能影响。JVM 已做多项优化:
立即学习“Java免费学习笔记(深入)”;
- 过滤无意义操作:跳过同一 Region 内部引用、空引用、常量池引用等
- 线程本地缓冲:G1 每个线程独享 SATB 队列,避免锁竞争
- 批量提交:队列满后才归并到全局集合,减少同步次数
- 可调参数:如
-XX:G1SATBBufferSize控制单个队列大小,默认 1KB,大内存应用可适当调高
和记忆集(Remembered Set)的关系
写屏障是“动作”,记忆集是“结果”。二者配合运作:
- 当老年代对象 A 的字段被赋值为新生代对象 B 时,写屏障触发
- 它检查 A 所在内存位置是否属于老年代、B 是否属于新生代 → 确认这是跨代引用
- 随后将 A 所在卡页标记为“脏”,或直接把 A 加入新生代对应的记忆集
- Minor GC 时,只遍历记忆集中登记的对象,不再扫描全部老年代

















