JVM通过记忆集、卡表和写屏障协同实现跨代引用收集,避免Minor GC时全量扫描老年代。记忆集逻辑记录跨代引用位置,卡表以512字节为单位标记脏卡页,写屏障实时更新卡表状态,GC仅扫描Dirty卡页中的跨代引用。
JVM 实现跨代引用收集,核心是避免 Minor GC 时扫描整个老年代,同时确保被老年代对象引用的年轻代对象不被误回收。它依赖“跨代引用极少”这一关键假说,用空间换时间,通过 记忆集(Remembered Set) 和其物理实现 卡表(Card Table) 配合 写屏障(Write Barrier) 协同工作。
记忆集是抽象概念,作用是记录“哪些非收集区域(如老年代)中存在指向当前收集区域(如年轻代)的引用”。Minor GC 时,GC Roots 不仅包含线程栈、静态变量等,还会加入记忆集中登记的跨代引用对象——这样就不必遍历全老年代。
卡表是记忆集最主流的物理实现,本质是一个字节数组,每个元素对应老年代中一块固定大小的内存区域(称为“卡页”,Card Page)。
addr,对应卡表下标为 addr >> 9(右移 9 位等于除以 512)0 表示该卡页未发生跨代引用;值为 1(Dirty)表示该卡页内至少有一个对象引用了年轻代每当老年代对象的字段被赋值(尤其是新引用指向年轻代对象),JVM 会在赋值指令前后插入一小段代码——这就是写屏障。它的职责是检测并标记相关卡页为 Dirty。
oldObj.field = youngObj 时,写屏障会计算 oldObj 所在卡页位置,并将对应卡表项设为 1一次年轻代 GC 并不从头扫描所有老年代对象,而是聚焦于卡表中标记为 Dirty 的卡页。