rset是g1收集器中每个region维护的“谁引用了我”的反向索引结构,通过写屏障异步更新卡页引用信息,使young gc和mixed gc能精准扫描跨region引用源,避免全堆遍历老年代。

记忆集(Remembered Set,RSet)不是供开发者手动调用或配置的 API,它由 JVM 自动维护,作用是支撑 G1 垃圾收集器高效处理跨 Region 引用——尤其在年轻代回收或混合回收时,避免扫描整个老年代。
它怎么起作用
RSet 是一种“谁引用了我”的反向索引结构,每个 Region 都有自己专属的 RSet,记录其他 Region 中指向本 Region 对象的引用位置。比如:
- Region A(老年代)中的对象 o1 持有对 Region B(年轻代)中对象 o2 的引用;
- 那么 Region B 的 RSet 就会记录一条:来自 Region A 的某几个 Card(卡页)存在对本 Region 的引用;
- 当触发年轻代 GC 时,G1 只需扫描这些被标记的 Card 对应的引用,而不用遍历整个老年代。
它靠写屏障自动更新
RSet 不是实时镜像,它的内容只在对象引用发生变更时,由 JVM 插入的写屏障(Write Barrier)触发更新:
- 每次执行类似
obj.field = otherObj这样的赋值操作时,JVM 会检查otherObj是否落在不同 Region; - 如果是,就标记源 Region 中对应 Card 为 dirty,并安排后台线程(Concurrent Refinement Thread)批量将该 Card 内的跨 Region 引用信息,归并进目标 Region 的 RSet;
- 这个过程是异步的,所以 RSet 总是“稍滞后但足够准确”。
它影响哪些 GC 行为
RSet 直接参与 G1 的根扫描(Root Scanning)阶段:
- 年轻代 GC(Evacuation Pause):把所有老年代中可能指向年轻代的引用来源(即 RSet 记录的 dirty Card)加入根集合,和栈、寄存器等一起扫描;
- 混合回收(Mixed GC):针对待回收的老年代 Region,同样依靠其 RSet 找出哪些其他 Region 持有对它的引用,确保不漏标;
- 如果没有 RSet,G1 就得保守地把整个老年代当作 GC Roots 的一部分,停顿时间会急剧上升。
它需要你关注什么
虽然不手动使用,但 RSet 的行为会影响应用表现,值得关注以下几点:
- 内存开销:RSet 存在堆外(Native Memory),大量跨 Region 引用(如老年代缓存持有大量新生代 DTO)会导致 RSet 占用数百 MB 甚至几 GB;
-
延迟风险:若写屏障产生的 dirty Card 太多,而
-XX:G1ConcRefinementThreads设置过小,Refine 线程跟不上,可能引发 Concurrent Mode Failure; -
可观测指标:可用
jstat -gc <pid></pid>查看CCSU(Concurrent RS Update)列,占比持续偏高说明 RSet 更新压力大。










