深入理解 G1 Mixed GC:触发条件、完整流程与高频误区

G1 收集器是目前 Java 服务端主流的分代收集器之一,它最大的特点是将堆划分为多个 Region,并通过 Young GC、Mixed GC、Full GC 三种回收动作配合工作。
其中 Mixed GC 是 G1 独有的回收机制,也是很多同学容易混淆的知识点。本文将从定义、触发条件、完整流程、是否一定执行、与 Young GC/Full GC 的区别等角度,系统梳理 G1 Mixed GC 的核心逻辑。


一、什么是 G1 Mixed GC

1.1 定义

Mixed GC 是 G1 收集器独有的回收动作,Parallel / CMS 等收集器均没有该概念。

它的核心作用是:

分批回收老年代垃圾,尽量降低 Full GC 发生的概率。

1.2 回收范围

Mixed GC 的回收范围包括:

  • 整个新生代:Eden + Survivor
  • 部分老年代 Region:只挑选垃圾占比最高、回收收益最大的一批老年代 Region

需要特别强调:

Mixed GC 不会回收全部老年代 ,每次只回收一部分 Region。
虽然它同时回收新生代,但新生代回收本身普通 Young GC 就能完成,Mixed GC 的出发点是解决老年代垃圾堆积问题。

1.3 算法与停顿

  • 算法:复制算法
  • 停顿:全程 STW,但通常比 Full GC 停顿时间短得多

二、触发前提:必须先完成一整套并发标记周期

很多同学误以为"老年代达到 45% 就会触发 Mixed GC",其实这是不准确的。

2.1 IHO 阈值的作用

G1 通过参数:

复制代码
-XX:InitiatingHeapOccupancyPercent

简称 IHO ,默认值为 45%。

它的含义是:

当老年代占用达到整个堆的 45% 时,触发并发标记周期,而不是直接触发 Mixed GC。

检测时机通常发生在 Young GC 的 STW 阶段内:

  1. Eden 满,触发 Young GC;
  2. Young GC 回收完新生代后,在 STW 尚未释放时检测老年代占用;
  3. 如果老年代占用超过 IHO 阈值,则启动并发标记周期;
  4. 如果未超过阈值,则只完成普通 Young GC,不启动标记周期。

因此:

  • IHO 阈值是并发标记周期的开关;
  • 并不是 Mixed GC 的开关;
  • 只有完整跑完并发标记周期,才可能执行 Mixed GC。

2.2 未达阈值的情况

如果老年代占用始终低于 IHO 阈值:

  • 只反复执行普通 Young GC;
  • 不会启动并发标记周期;
  • 老年代垃圾不会被扫描,也不会被回收;
  • 自然也不会有 Mixed GC。

三、完整并发标记周期时序

当老年代占用达到 IHO 阈值后,G1 会启动一整套并发标记周期,主要包括以下 4 个阶段:

3.1 初始标记(Initial Mark,STW)

初始标记会挂靠在一次 Young GC 的 STW 上。

过程:

  1. Eden 满,触发 Young GC;
  2. Young GC 回收新生代;
  3. 在同一个 STW 中检查老年代占用超过阈值;
  4. 执行初始标记,标记 GC Roots 直接可达对象;
  5. STW 结束。

注意:

绝大多数 Young GC 不会执行初始标记。
只有老年代达到阈值的那一次 Young GC,才会复用其 STW 执行初始标记。

3.2 并发标记(Concurrent Mark,无 STW)

并发标记阶段,GC 后台线程与业务线程并行执行,遍历堆对象图,做三色标记,识别老年代存活对象与垃圾对象。

重点:

  • 并发标记可以被 Young GC 打断;
  • Eden 一旦满,就会触发 STW 的 Young GC,并发标记线程暂停;
  • Young GC 执行完毕后,并发标记线程从断点继续执行;
  • 使用 SATB(Snapshot At The Beginning)原始快照写屏障,记录 Young GC 复制、晋升带来的对象变动,防止漏标。

3.3 最终标记(Remark,STW)

最终标记阶段会处理 SATB 队列、RSet 记忆集等,修补并发标记期间,包括中途穿插的 Young GC 产生的全部漏标。

完成后,整个堆所有对象的存活状态基本标记完成。

3.4 筛选回收:Mixed GC(STW)

基于前面标记得到的老年代 Region 垃圾统计信息,挑选回收收益最高的一批老年代 Region,连同当前已经被业务重新填满的新生代,一起复制回收。

关键点:

当初开启标记周期的那次 Young GC 已经清空了新生代,但在并发标记和 Remark 期间,业务线程持续创建对象,Eden 又会被重新填满。
因此 Mixed GC 必须同时处理新生代。

完整时序可简化为:

复制代码
Eden 满
  ↓
Young GC(清理新生代)
  ↓ 检测老年代超过 IHO?
  ├── 否 → 普通 Young GC 结束,不启动并发标记
  └── 是 → 同一个 STW 执行初始标记
              ↓
         并发标记(可被多次 Young GC 打断)
              ↓
         Remark 最终标记
              ↓
         Mixed GC(回收新生代 + 部分老年代 Region)

四、Mixed GC 一定执行吗?两种结局

即使老年代达到 IHO 阈值,启动了并发标记周期,Mixed GC 也不一定 100% 执行。

4.1 正常流程:成功执行 Mixed GC

如果标记周期顺利完成,G1 会根据标记结果分批执行 Mixed GC:

  • 每次 Mixed GC 回收一部分老年代 Region;
  • 释放内存,老年代占用下降;
  • 后续对象继续晋升,老年代再次达到 IHO 阈值;
  • 开启新一轮标记周期,再次执行 Mixed GC。

这样循环往复,尽量不发生 Full GC。

一次标记周期结束后,通常会触发多次 Mixed GC,每次只回收一部分老年代 Region,逐步释放空间。

4.2 Mixed GC 不会执行的两个场景

场景 1:老年代占用始终低于 IHO 阈值
  • 不启动并发标记周期;
  • 只会反复执行普通 Young GC;
  • 老年代垃圾不会被清理;
  • 不会执行 Mixed GC。
场景 2:对象晋升速度过快,老年代提前耗尽
  • 已经启动并发标记周期;
  • 但并发标记还未完成,或 Mixed GC 还没机会执行;
  • 大量对象晋升,老年代 Region 被耗尽;
  • 直接退化触发 Full GC;
  • Mixed GC 被跳过。

通俗理解:

IHO = 45% 相当于警戒线。
老年代到 45%,拉起"垃圾清点小组"即并发标记周期,清点哪些老年代 Region 垃圾多。
清点全部结束后,才安排分批打扫,即 Mixed GC。
如果还没清点完,房间直接堆满了,就只能全屋大扫除,即 Full GC,分批打扫就不做了。
房间没到警戒线,清点小组根本不会上班。


五、Young GC / Mixed GC / Full GC 对比

GC 类型 STW 回收范围 触发条件 核心特点
Young GC ✅ STW 仅新生代 Eden + Survivor Eden 占满 复制算法;只清理新生代;对象晋升老年代;不回收老年代垃圾;可以打断并发标记
Mixed GC ✅ STW 新生代 + 部分老年代 Region 完整并发标记周期结束后 G1 独有;分批清理老年代;用于规避 Full GC;不会回收全部老年代
Full GC ✅ 长时间 STW 新生代 + 老年代 + 元空间全部 老年代内存耗尽 最坏兜底;通常单线程整理;生产环境极力避免

六、高频误区与坑点

误区 1:Mixed GC 会紧跟在触发标记的那次 Young GC 之后立即执行

错误。

Mixed GC 不会马上执行。

在初始标记之后,还要经历并发标记、Remark,期间可能发生多次普通 Young GC。

只有完整标记周期结束后,才会执行 Mixed GC。

误区 2:并发标记期间不能发生 Young GC

错误。

Young GC 优先级更高,可以打断并发标记。

并发标记线程会被暂停,Young GC 执行完后从断点继续。

G1 通过 SATB 写屏障保证标记正确性。

误区 3:Mixed GC 只回收老年代

错误。

Mixed GC 必须同时回收新生代。

因为并发标记和 Remark 期间,业务线程不断产生新对象,Eden 会被重新填满。

所以 Mixed GC 的回收范围是:新生代 + 部分老年代 Region。

误区 4:Mixed GC 会一次性清空全部老年代

错误。

Mixed GC 每次只回收一部分老年代 Region,通常需要多次 Mixed GC 才能逐步释放老年代空间。

这也是 G1 能够控制停顿时间的关键设计之一。


七、总结

G1 Mixed GC 的核心逻辑可以概括为以下几点:

  1. Mixed GC 是 G1 独有,用于分批回收老年代,避免 Full GC。
  2. IHO 阈值默认 45% 是并发标记周期的开关,不是 Mixed GC 的开关。
  3. 只有完整跑完:初始标记 → 并发标记 → Remark,才可能执行 Mixed GC。
  4. 并发标记可以被 Young GC 打断,SATB 保证不漏标。
  5. Mixed GC 回收新生代 + 部分老年代 Region,不是全部老年代。
  6. 若对象晋升速度过快,老年代耗尽,会直接 Full GC,跳过 Mixed GC。

理解这些逻辑,就能准确回答 G1 Mixed GC 的触发条件、执行流程以及常见误区。

相关推荐
认真的小羽❅3 小时前
ThreadLocal 在虚拟线程间要小心
java·开发语言·jvm
今天的砖头有点烫手啊4 小时前
Java 8 Stream 原理拆解:惰性求值与并行流陷阱
java·jvm
用户094248568035 小时前
第37章:JVM极端场景调优与生产故障复盘方法论
java·jvm
南极企鹅7 小时前
生产环境gc文件分析
java·jvm·算法
欣栀♡16 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
czt_java17 小时前
JVM 核心基础|运行时数据区、类加载机制、GC 分代回收
jvm
Web极客码20 小时前
Python Ollama 本地 Agent 入门与订单查询实践
开发语言·jvm·python
y = xⁿ20 小时前
一文掌握JVM
jvm
顺风尿一寸1 天前
从 FileOutputStream 到 int fd:一次 JNI 字段读写全链路源码剖析
linux·jvm