G1 GC 的核心基础:Region 模型的补充细节

一、G1 GC 的核心基础:Region 模型的补充细节

原文对Region的讲解很清晰,这里补充两个支撑G1高效运作的关键数据结构:

1.1 Remembered Set (RSet) ------ 分区独立的引用"账本"

G1之所以能独立回收某个Region,而不需要扫描全堆,核心就是RSet。

  • 作用:每个Region都有一个RSet,记录着"有哪些Region里的对象,引用了本Region中的对象"。

  • 好处 :在回收Region时,GC线程只需扫描该Region的RSet,就能知道哪些外部对象在引用它,从而找到GC Roots。这彻底打破了传统收集器扫描整个代(如整个老年代)的瓶颈。

  • 代价:维护RSet需要额外内存(约占堆内存的5%~10%),且当引用关系变化时(比如对象赋值),需要同步更新RSet。这是G1对写屏障(Write Barrier)开销的主要来源。

1.2 Humongous 大对象区的特殊处理

原文提到了大对象区(H区),这里补充它的回收规则:

  • 判定 :当对象大小 > 50% G1HeapRegionSize时,会被分配为"巨型对象"。如果对象大小超过一个Region,会占用连续的多个H区。

  • 回收 :巨型对象不会 在Young GC中被回收。只有在Mixed GC的并发标记阶段 发现它不再存活,或者在Full GC时才会被回收。

  • 风险 :如果大对象频繁创建且生命周期短,很容易填满老年代,诱发Full GC。调优时建议通过jmap -histo查看大对象,必要时调整G1HeapRegionSize(让大对象落入普通老年代Region,享受Mixed GC回收),或从代码层面优化。


二、回收类型进阶:Mixed GC 的内部阶段细化

原文将Mixed GC分为并发标记和筛选回收两步。实际上,在JDK 8u40之后,G1的Mixed GC在"筛选回收"阶段内部也做了精细化拆分,这对理解停顿非常关键。

2.1 Mixed GC 的完整阶段(JDK 8u40+)

除了原文提到的"并发标记"五子阶段,后续的"筛选回收"阶段在G1中被称为清理阶段,它实际上包含两个子阶段:

  1. 并发标记阶段(同原文,略)

  2. 清理阶段:

    • STW 清理 :计算每个老年代Region的垃圾占比,并排序。这个阶段只做统计,不回收对象。G1会预测如果回收某个Region需要多长时间。

    • 并发清理 :清空那些没有存活对象的Region(空Region),将它们放回空闲列表。此阶段无STW。

    • STW 筛选回收 :这才是真正的"复制存活对象"阶段。G1根据MaxGCPauseMillis目标,从排序后的Region列表中选择"性价比"最高的若干个(垃圾占比高且预测停顿短),进行复制和回收。注意,G1通常不会在一次Mixed GC中回收所有待回收的老年代Region,而是分多次(多轮)进行,直到老年代内存占比低于阈值。

2.2 为什么会有"多次"Mixed GC?

这是G1实现"软实时"目标的关键:

  • 一次并发标记结束后,会产生一个"待回收Region列表"。

  • 后续的GC暂停(可能连续多次)都是[GC pause (G1 Mixed Generation)]。

  • 每次暂停只回收列表中的一小部分,直到列表为空,或者老年代内存占比降至目标值以下。


三、关键机制深挖:停顿预测模型与写屏障

3.1 停顿预测模型

G1的强大之处在于它有一个停顿预测模型。它不是简单地"回收几个Region",而是基于历史数据动态调整。

  • 原理 :JVM会记录每次回收时各阶段的耗时(如扫描RSet耗时、复制对象耗时)。在下次回收时,它会根据MaxGCPauseMillis目标,计算"当前我最多能回收多少个Region,才能保证99%的概率不超时"。

  • 调优启示 :如果MaxGCPauseMillis设置得过短(如<50ms),G1会极力压缩回收量,导致每次回收的垃圾太少,从而频繁触发Young GC和Mixed GC,整体吞吐量下降,甚至可能因回收速度跟不上分配速度而诱发Full GC。

3.2 SATB (Snapshot-At-The-Beginning) 标记算法

原文提到的并发标记阶段用到了SATB算法,它是G1能实现"并发标记"的底层支撑。

  • 作用:在并发标记开始时,SATB会为当前存活对象拍一张"逻辑快照"。

  • 逻辑 :在并发标记期间,如果某个原本存活的对象(在快照中)的引用被用户线程修改(比如被置为null),G1会通过写屏障 记录下这个对象,确保在最终标记阶段仍然将其视为存活。

  • 结果 :这种"保守"的标记策略会导致浮动垃圾。即并发标记期间变成垃圾的对象,本次GC不会回收,只能等到下一次Mixed GC再处理。这是G1内存占用有时看起来"回收不彻底"的原因。


四、实战调优:从"拍脑袋"到"有依据"

原文给出了参数列表,这里补充这些参数的配合逻辑和反直觉场景。

4.1 核心参数组合拳:IHOP 与 MaxGCPauseMillis 的博弈
  • -XX:InitiatingHeapOccupancyPercent (IHOP)

    • 默认值45% :指老年代占整个堆的比例。当老年代占用超过45%时,触发并发标记。

    • 调优场景 :如果GC日志中出现**"Concurrent Cycle"因"to-space overflow"中断(即并发标记中途老年代被填满)** ,说明IHOP设置得太高,Mixed GC启动太晚。应适当降低IHOP(如38%),给回收留出缓冲时间。

    • 注意 :在JDK 10之后,G1引入了自适应IHOP (-XX:-G1UseAdaptiveIHOP),JVM会自动根据对象分配速率调整触发阈值,通常比手动设置更优。

4.2 线程数:ParallelGCThreads 与 ConcGCThreads

这是调优中最容易忽略的参数。

  • 公式:

    • ParallelGCThreads(STW阶段):如果CPU核数>8,公式为 8 + (核数-8)*5/8。通常不需要改。

    • ConcGCThreads(并发阶段):通常为 ParallelGCThreads 的 1/4。

  • 问题场景 :在CPU核数少(如2核)的容器环境中,如果ConcGCThreads默认为1,并发标记可能会占用大量CPU资源,导致业务线程争抢。此时可以考虑手动降低ConcGCThreads为1(已是下限),或通过容器CPU配额限制。

4.3 避免Full GC的"三板斧"

当发生Full GC时,不要只盯着内存大小,按顺序排查:

  1. 看日志 :是to-space overflow(并发回收速度跟不上分配)?还是concurrent mode failure(并发标记阶段老年代就满了)?

    • 前者:说明MaxGCPauseMillis太严苛,导致回收量小,或IHOP太高启动晚。

    • 后者:说明对象分配速率太高,考虑增大堆内存,或优化对象分配。

  2. 看大对象 :-XX:+PrintGCDetails 是否频繁出现 Humongous Allocation?如果是,考虑调整G1HeapRegionSize,将大对象"打散"进普通老年代。

  3. 看元空间 :G1的Full GC也可能由元空间(Metaspace)满触发。确保设置了 -XX:MaxMetaspaceSize,避免元空间无限制增长。


五、总结补充:G1的适用边界

原文总结得很好。最后补充一点关于"选型"的思考:

  • G1的优点 :它的设计目标是**"软实时"** ,即在可控的停顿时间(默认200ms) 内,完成尽可能多的垃圾回收。适合堆内存大(4GB+)、要求低延迟的应用。

  • G1的弱点:

    • 维护成本高:RSet、卡表等元数据占用较多堆外内存。

    • 吞吐量稍低 :相比于Parallel Scavenge(吞吐量优先),G1由于写屏障和并发标记的开销,整体吞吐量(业务时间/总时间)会低约5%~10%。

    • 小堆表现不佳:在堆内存较小(<4GB)且对吞吐量敏感的场景,Parallel Scavenge有时表现更好。

相关推荐
hetao173383716 小时前
2026-10-03~04 hetao1733837 的刷题记录
c++·算法
\光辉岁月/17 小时前
5.java-数组
java·开发语言
谢亮_vipxieliang17 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
Zootopia62617 小时前
飞行力学知识梳理1|飞行性能与稳定性
人工智能·python·算法·机器学习·无人机·学习方法·信息与通信
longlongzihan17 小时前
从LeetCode 70. 爬楼梯看动态规划:如何精准推导状态转移方程?
c++·算法·leetcode·动态规划
郑州光合科技余经理17 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
2601_9620715717 小时前
类变量和全局变量的生命周期有什么区别?
java·开发语言·jvm
code2cat18 小时前
Java进阶篇之AtomicReference:一次替换引用,读到一份完整状态
java·并发编程·不可变对象·atomicreference
专业程序开发源18 小时前
springboot简历管理系统81389-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
Wang's Blog18 小时前
Java 项目实战: 外卖平台优化-前后端分离开发模式与工程拆分
java·开发语言