怎么通过分析 **G1 记忆集(RSet)**更新逻辑理解跨代引用对增量回收效率的影响

大磊酱_4341

大磊酱_4341

2026-04-29

727人浏览

原创

g1的rset更新必须在写屏障中触发,因为跨代引用变更随机高频,需实时捕获每次跨region赋值(如老年代修改对年轻代字段的引用),以避免young gc时扫描整个老年代导致停顿失控;rset通过后置写屏障记录源card地址实现“延迟归集+精准定位”,禁用写屏障将漏记引用引发对象误回收。

怎么通过分析 **g1 记忆集(rset)**更新逻辑理解跨代引用对增量回收效率的影响

为什么 G1 的 RSet 更新必须在写屏障中触发

因为跨代引用的变更(比如老年代对象修改了对年轻代字段的引用)是随机、高频且不可预测的,如果等到 Young GC 时再扫描整个老年代找引用,停顿时间就失控了。RSet 的本质是「延迟归集 + 精准定位」:不实时维护完整引用关系,而是靠写屏障捕获每一次跨代写操作,在对应目标 Region 的 RSet 中记录下源 Card 的地址。

常见错误现象是误以为 RSet 是 GC 时才构建的——实际上它在每次 obj.field = otherObj 发生跨代赋值时就被更新(前提是 otherObj 在不同 Region)。若禁用写屏障(如通过 -XX:-UseCondCardMark),RSet 就会漏记,Young GC 可能漏扫存活对象,导致提前回收或崩溃。

  • 写屏障类型:G1 使用的是「后置写屏障(Post-barrier)」,即赋值完成后再执行 RSet 插入逻辑
  • 插入粒度:不是记录对象地址,而是记录「源对象所在 Card 的起始地址」,一个 Card 默认 512 字节
  • 并发安全:RSet 底层是并发哈希表,多线程写入时有锁或无锁分段机制,但高竞争下仍可能成为瓶颈

Young GC 时 RSet 扫描如何影响 STW 时间

Young GC 的根扫描阶段必须检查两类 Roots:一是线程栈/静态变量等原生 Roots;二是所有被老年代引用的年轻代对象——这部分就靠遍历当前 Eden/Survivor Region 对应的 RSet 来完成。RSet 越大、条目越多,扫描耗时越长,直接抬高 STW。

典型性能陷阱是巨型对象(Humongous Object)频繁被老年代引用:一个 Humongous Region 可能横跨多个 Card,而每个被引用的 Card 都会在多个目标 Region 的 RSet 中留下条目,造成 RSet 膨胀。实测中,RSet 占用可达堆内存的 10%~20%,且其扫描开销与条目数基本呈线性关系。

  • 可观察指标:G1RSetScannedCards(已扫描 Card 数)、G1RSetUpdates(写屏障触发次数)可通过 -XX:+PrintGCDetails 查看
  • 参数干预:-XX:G1RSetRegionEntries=N 控制单个 Region 的 RSet 最大条目数,超限后退化为全 Card 扫描(慎调)
  • 避免方式:减少老年代对象持有年轻代对象的强引用,尤其避免缓存类结构中混存新旧对象

混合回收(Mixed GC)中 RSet 如何支撑跨老年代 Region 引用

CMS 只需处理「老→年轻」引用,所以卡表(Card Table)够用;但 G1 的 Mixed GC 要回收部分老年代 Region,就必须知道「哪些其他老年代 Region 持有对本 Region 的引用」——这就要求 RSet 不仅记录老→年轻,还要记录老→老、甚至年轻→老(用于反向追踪晋升对象是否还被引用)。

这意味着每个 Region 的 RSet 实际上是一张小范围的「引用关系索引表」。当 G1 决定回收某个 Old Region A 时,它不会去扫描整个堆,而是只查 A 的 RSet,拿到所有可能引用 A 的源 Region 列表,再精准扫描这些源 Region 中对应的 Card。这是 G1 实现增量式老年代回收的底层前提。

  • RSet 构建成本被均摊到运行期:写屏障开销换来了 GC 阶段的确定性
  • 注意 -XX:G1ConcRefinementThreads 参数:控制并发 Refinement 线程数,负责把写屏障缓冲区中的 dirty card 批量刷入 RSet;设太小会导致 RSet 更新滞后,Young GC 可能漏扫
  • Humongous Region 的 RSet 默认不启用(因其本身被视为老年代一部分),但若它被其他 Region 引用,仍会通过普通 Region 的 RSet 记录

RSet 分析工具链和关键日志字段

光看 GC 日志不够,得结合 RSet 相关计数器判断是否成为瓶颈。JDK 自带的 jstat 和 GC 日志中的特定字段最实用。

开启详细 RSet 统计:启动时加 -XX:+PrintAdaptiveSizePolicy -XX:+PrintGCDetails -XX:+UseStringDeduplication,重点关注每轮 Young GC 后的 RSet 行:

[GC Worker Start (ms):  12345.678]
[Ext Root Scanning (ms):  2.123]
[RSet Scanning (ms):  18.456]   
  • RSet Scanning (ms) 超过总 STW 30%,说明 RSet 已成主要瓶颈
  • jstat -gc <pid></pid> 中的 EC(Eden Capacity)、EU(Eden Used)变化不大但 YGC 频次上升,常伴随 RSet 条目暴增
  • 用 jcmd <pid> VM.native_memory summary scale=MB</pid> 查看 Internal 区域内存占用,若持续增长,大概率是 RSet 元数据膨胀

RSet 本身不是黑盒,它的设计取舍非常明确:用运行期写屏障的小代价,换取 GC 阶段的可控性。真正难的是在业务代码里识别出那些无意中制造跨代引用热点的地方——比如一个长期存活的 Map 缓存了刚 new 出来的 DTO,这种引用关系会一直拖着 DTO 晋升,同时不断往 RSet 塞新条目。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

2026.10.16

20

10

OpenJDK怎么配置Java开发环境
OpenJDK怎么配置Java开发环境

怎样用OpenJDK搭建开发环境?本专题介绍版本选择、下载安装、环境变量配置和编译运行验证,帮助你理清JDK的作用及基本配置流程,并通过简单程序检查环境是否可用,为后续项目开发做好准备。

2026.10.16

20

10

OpenJDK怎么部署Java应用
OpenJDK怎么部署Java应用

Java项目开发完成后,怎样部署到目标环境并正常运行?本专题介绍OpenJDK应用部署流程,涵盖运行环境准备、版本兼容检查、应用打包、启动参数配置和日志查看,帮助你理清部署前后的操作顺序,减少环境差异造成的运行问题。

2026.10.16

0

10

Revel框架模板渲染与表单验证
Revel框架模板渲染与表单验证

本专题围绕Revel框架视图层开发,介绍模板语法、变量输出、布局复用、表单提交、参数绑定与验证规则,帮助开发者完成安全规范的页面交互。

2026.10.16

20

15

Revel框架MVC路由与控制器
Revel框架MVC路由与控制器

本专题聚焦Revel框架MVC核心机制,讲解路由定义、控制器Action、参数绑定、请求响应和反向路由,并结合常见场景梳理接口请求处理流程。

2026.10.16

20

15

Revel框架入门与安装配置
Revel框架入门与安装配置

本专题从Revel框架环境搭建讲起,系统介绍命令行工具安装、项目创建、目录结构、配置文件及热代码重载,帮助开发者快速完成首个Go Web应用。

2026.10.16

20

15

Linux 新手先学哪些命令
Linux 新手先学哪些命令

Linux新手命令专题汇集终端基础、文件目录操作、文本查看、软件安装和权限管理等入门文章,帮助按实际任务练习常用命令并建立命令行操作习惯。

2026.10.15

40

20

MySQL 忘记密码怎么办
MySQL 忘记密码怎么办

MySQL忘记密码专题汇集不同版本与系统下的root密码重置方法,覆盖MySQL 5.7、8.0、Mac、Linux及本地集成环境,并提示恢复权限与重新登录的检查步骤。

2026.10.15

20

18

Vue.js 中 ref 和 reactive 的区别
Vue.js 中 ref 和 reactive 的区别

Vue.js中ref和reactive的区别专题汇集两种响应式API的定义、访问方式、对象解构、侦听与性能取舍等文章,帮助结合实际状态类型选择合适写法。

2026.10.15

20

20

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.8万人学习