写屏障是jvm在老年代对象引用新生代对象时触发的轻量逻辑,用于标记卡表脏卡,从而压缩minor gc扫描范围。它只在老→新赋值时激活,通过硬件指令快速更新卡表,使gc仅需检查脏卡页内的对象作为额外roots。

写屏障是 JVM 在对象引用发生变更时插入的一段轻量级逻辑,它的核心作用不是拦截或阻止写操作,而是“感知并记录”跨代引用的产生,从而驱动卡表状态更新。理解它,关键在于看清它在“谁改、改哪、怎么记”三个环节上的设计意图。
写屏障触发的时机很具体
它只在老年代对象的字段被赋值为新生代对象时才激活。比如一个长期存活的 OrderService 实例(在老年代),在处理请求时 new 出一个临时的 OrderDetail(进入 Eden 区),然后执行 service.detail = detail —— 正是这行赋值,触发写屏障。
它不关心新生代内部引用、也不处理老年代到老年代的赋值,更不会在读取字段时介入。这种精准触发,保证了开销可控。
卡表靠写屏障来“打标记”
卡表本质是一张极简的字节数组,每项对应老年代中一块 512 字节的内存区域(即一张“卡”)。写屏障的任务就是:一旦发现某次赋值让老年代某个对象持有了新生代引用,就立刻计算出该对象所在的卡页索引,并把对应字节设为 1(标记为“脏卡”)。
这个过程由底层硬件指令支持(如 CAS 或 store-store barrier),通常只需几纳秒,业务线程几乎无感。
卡表的价值体现在 Minor GC 的扫描范围压缩
没有写屏障和卡表,每次年轻代回收都得扫描整个老年代,确认有没有对象正引用着 Eden 或 Survivor 中的对象——这对几 GB 的老年代来说,STW 时间会不可接受。
有了卡表,Minor GC 只需检查所有被标记为“脏”的卡页内对象,从中找出真正持有新生代引用的那些,把它们作为额外的 GC Roots。这样既保证了可达性分析正确,又把扫描量从 GB 级压缩到 KB 级。
写屏障与记忆集的关系
卡表是记忆集(Remembered Set)的一种具体实现形式,专用于解决“老→新”跨代引用问题。G1 收集器则用更细粒度的 Region 级记忆集替代了传统卡表,但底层逻辑一致:写屏障捕获引用变更,记忆集记录影响范围,GC 依此缩小扫描边界。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











