记忆集(RSet)是JVM为高效处理跨代引用而设计的反向索引表,按Region粒度记录老年代中指向本年轻代Region的引用源Region,结合写屏障更新,在Minor GC时仅扫描相关老年代Region,避免全老年代扫描。

记忆集(Remembered Set,简称 RSet)是 JVM 在分代垃圾回收(尤其是 G1、ZGC 等增量/并发收集器)中用于高效处理跨代引用的核心数据结构。它不直接“解决”跨代引用,而是**精准记录哪些老年代区域(Region)中存在指向年轻代对象的引用**,从而在年轻代 GC 时,无需扫描整个老年代,就能准确识别并加入 GC Roots,避免漏标。
为什么跨代引用对年轻代 GC 构成挑战
在分代回收中,年轻代 GC(Minor GC)默认只清理年轻代,但若老年代中某个对象持有对年轻代对象的引用(即跨代引用),该年轻代对象就不能被回收。JVM 若每次 Minor GC 都扫描全部老年代,开销极大。记忆集正是为规避这种全局扫描而设计的索引机制。
记忆集如何工作:写屏障 + 区域粒度映射
记忆集本质是一个“反向索引表”,按年轻代 GC 的单位(如 G1 中的 Region)组织,记录哪些老年代 Region 中有指针指向本 Region 内的对象。其构建依赖两个关键机制:
-
写屏障(Write Barrier):当程序执行类似
obj.field = youngObj(老年代对象修改字段,指向年轻代对象)时,JVM 在赋值前后插入一段轻量代码(写屏障),捕获该跨代写操作,并将“源 Region”(老年代中 obj 所在 Region)加入目标年轻代 Region 对应的记忆集中。 - 区域化管理(Per-Region RSet):G1 将堆划分为固定大小 Region,每个 Region 维护自己的记忆集。例如,Region A 是年轻代,它的 RSet 可能包含 [R102, R105, R201] —— 表示老年代中的 Region 102、105、201 内有对象引用了 A 中的对象。
年轻代 GC 时如何利用记忆集
触发 Minor GC 时,GC 线程只需做两件事:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 将年轻代所有对象标记为“待扫描”;
- 遍历当前年轻代 Region 的记忆集,定位到对应的老年代 Region,仅扫描这些 Region 中的跨代引用字段(如对象头、OopMap、卡表辅助信息等),把被引用的年轻代对象加入根集合(Roots)。
这样既保证了可达性分析的完整性(不会误回收存活对象),又大幅减少了扫描范围——从“全老年代”缩小到“少量相关 Region”。
补充说明:卡表(Card Table)与记忆集的关系
在 CMS 等早期分代收集器中,常用更粗粒度的卡表(Card Table)来近似解决跨代引用:将老年代划分为 512 字节一张的卡页,某卡页内发生跨代写入,就标记该卡页为 dirty。Minor GC 时扫描所有 dirty 卡页。而 G1 的记忆集是卡表的精细化升级——它不仅知道“哪张卡脏了”,还知道“哪几个 Region 里有引用”,支持更精确的并发标记与混合回收。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










