g1 gc按region分片回收,young gc仅回收所有eden region及部分survivor region,mixed gc增量回收高垃圾密度old region,humongous对象直接分配独立region且不参与常规gc,full gc为单线程兜底机制。

G1 GC 不是“一次性扫完整个堆”,而是按 Region 分片回收,每次只挑垃圾最多、收益最高的几个 Region 来处理——所以停顿时间可控,但代价是并发标记开销大、内存占用略高。
Young GC 怎么选回收集(CSet)
Young GC 只处理 Eden 和 Survivor 区,不碰老年代。它会把所有当前标记为 Eden 的 Region 全部加入回收集(CSet),再根据 -XX:MaxGCPauseMillis 预期目标,动态决定是否多收几个 Survivor Region 来“凑够”空间。
- 触发条件通常是
Eden区填满,或 G1 的预测模型判断下一次分配大概率失败 - 存活对象要么复制到空闲
SurvivorRegion,要么直接晋升到OldRegion(年龄达标或 Survivor 空间不足) - 晋升时若目标
OldRegion 不够用,可能触发Concurrent Marking提前启动,甚至退化成 Full GC
Mixed GC 为什么不是“全老年代扫描”
Mixed GC 是 G1 的核心差异化行为:它在 Young GC 基础上,额外加入一批已标记为“可回收价值高”的 Old Region。这些 Region 来自上一轮并发标记的结果,G1 会按垃圾密度排序,优先挑最“脏”的几个。
- 不会扫描整个老年代,只处理
Collection Set中的 Old Region - 每个 Mixed GC 回收的 Old Region 数量受
-XX:G1MixedGCCountTarget和-XX:G1OldCSetRegionThresholdPercent控制 - 如果某次 Mixed GC 后,仍有大量可回收 Old Region,G1 会继续调度下一轮;但如果连续几次都收不干净,可能触发
Concurrent Mode Failure,进而导致 Full GC
Humongous 对象怎么影响回收节奏
大小超过半个 Region 的对象(比如大数组、长字符串)会被直接分配到 Humongous Region。这类 Region 占用整块连续空间,且只能存放一个对象。
-
HumongousRegion 不参与 Young GC,也不进 Mixed GC 的常规 CSet;只有当它所含对象不可达,且发生 Full GC 或 CMS 后才会被回收 - 频繁分配 Humongous 对象容易造成堆碎片和 Region 浪费(比如一个 1.2MB 对象占了 2MB Region)
- 可通过
-XX:G1HeapRegionSize调整 Region 大小来缓解,但注意:该值在 JVM 启动时固定,不能运行时改
Full GC 在 G1 里意味着什么
G1 的 Full GC 是兜底机制,一旦触发就是单线程、STW、标记-整理式全局回收——性能表现接近 Serial GC,停顿往往长达数秒。
- 常见诱因包括:
Concurrent Mode Failure(并发标记没跑完,老年代就撑不住了)、Evacuation Failure(疏散时 Survivor/Old 都没空间放存活对象)、Humongous Allocation Failure(申请 Humongous 对象但找不到连续空闲 Region) - OpenJDK 17+ 引入了
-XX:+G1EagerReclaimHumongousObjects,允许在并发标记阶段提前回收无引用的 Humongous 对象,降低 Full GC 概率 - 真正难调的不是参数本身,而是识别哪类对象在“悄悄吃掉”老年代空间——建议开启
-Xlog:gc+heap+region=debug观察 Region 类型分布
Region 划分让 G1 可控停顿,但也把问题从“什么时候 GC”转移到了“哪些 Region 该收、什么时候收够”,而并发标记链路长、状态依赖多,稍有偏差就容易滑向 Full GC。实际调优中,比调参更重要的是看清对象生命周期和分配模式。











