![图片[1]-12 JVM垃圾回收机制(下):G1回收器与调优实战-速优课](http://www.suyouke.com/wp-content/uploads/2026/07/doubao_img_2304x1728_20260722_174942-1-1024x768.png)
在上一篇中,我们介绍了垃圾回收的基本原理和常用算法。本文将深入探讨 G1 回收器的设计原理、工作流程以及 JVM 调优的实战技巧。
一、G1 回收器的设计目标
G1 的核心特点
G1(Garbage First)是 Java 9 默认的垃圾回收器,设计目标是:
- 可预测停顿时间:可以设置最大停顿时间目标
- 高吞吐量:在保证停顿时间的前提下最大化吞吐量
- 无内存碎片:使用标记-整理算法
- 支持大堆内存:适用于 4GB 以上的堆内存
G1 与其他回收器的对比
| 回收器 | 适用场景 | 停顿时间 | 内存碎片 |
|---|---|---|---|
| Serial | 客户端、小堆 | 较长 | 有 |
| Parallel | 服务器、追求吞吐量 | 较长 | 有 |
| CMS | 追求低延迟 | 较短 | 有 |
| G1 | 大堆、追求可控延迟 | 可控 | 无 |
| ZGC | 超大堆、追求极低延迟 | 极低(<10ms) | 无 |
二、G1 的内存布局
Region 分区
G1 将堆内存划分为多个大小相等的 Region:
┌─────────────────────────────────────────────────────────────┐
│ G1 Heap Memory │
├─────────────────────────────────────────────────────────────┤
│ Region 0 │ Region 1 │ Region 2 │ ... │ Region N │
│ (Eden) │ (Survivor) │ (Old) │ │ (Humongous)│
└─────────────────────────────────────────────────────────────┘
Region 类型
| 类型 | 说明 |
|---|---|
| Eden | 新生代区域,新对象分配在此 |
| Survivor | 新生代区域,存放存活的年轻对象 |
| Old | 老年代区域,存放存活时间较长的对象 |
| Humongous | 存放超大对象(超过 Region 大小的一半) |
Region 大小
- 默认根据堆大小自动计算(1MB ~ 32MB)
- 可以通过
-XX:G1HeapRegionSize参数手动设置
三、G1 的工作流程
新生代收集(Young GC)
触发条件:
- Eden 区满时
流程:
- 扫描根:扫描 GC Roots
- 更新 Remembered Set:处理跨代引用
- 复制存活对象:将存活对象复制到 Survivor 区或 Old 区
- 清空 Eden 区:释放内存
并发标记周期
触发条件:
- 老年代占用率达到阈值(默认 45%)
流程:
- 初始标记(Initial Mark):
- 暂停所有应用线程
- 标记 GC Roots 直接引用的对象
- 记录 Remembered Set
- 并发标记(Concurrent Mark):
- 与应用线程并发执行
- 遍历对象图,标记所有存活对象
- 使用 SATB(Snapshot-At-The-Beginning)技术
- 最终标记(Final Mark):
- 暂停所有应用线程
- 处理并发阶段新增的引用
- 筛选回收(Live Data Counting & Evacuation):
- 计算每个 Region 的存活对象比例
- 选择回收价值最高的 Region
- 复制存活对象,释放空间
Mixed GC
在并发标记周期完成后,G1 会进行多次 Mixed GC:
- 回收 Old 区中存活对象比例低的 Region
- 同时进行新生代收集
- 逐步回收老年代垃圾
四、G1 的关键技术
Remembered Set
用于记录跨代引用,避免全堆扫描:
- 每个 Region 有一个 Remembered Set
- 记录其他 Region 指向本 Region 的引用
- 新生代收集时只需扫描 Remembered Set
SATB(Snapshot-At-The-Beginning)
确保并发标记的准确性:
- 在标记开始时创建对象图的快照
- 并发阶段新增的对象视为存活
- 并发阶段删除的对象需要特殊处理
停顿预测模型
G1 使用统计模型预测回收时间:
- 根据历史回收数据预测每个 Region 的回收时间
- 选择回收时间总和不超过目标停顿时间的 Region
- 动态调整回收策略
五、G1 的调优参数
基本参数
| 参数 | 说明 | 默认值 |
|---|---|---|
-XX:+UseG1GC | 启用 G1 回收器 | Java 9+ 默认 |
-Xms | 初始堆大小 | 物理内存的 1/64 |
-Xmx | 最大堆大小 | 物理内存的 1/4 |
-XX:MaxGCPauseMillis | 最大停顿时间目标 | 200ms |
高级参数
| 参数 | 说明 | 默认值 |
|---|---|---|
-XX:G1HeapRegionSize | Region 大小 | 自动计算 |
-XX:InitiatingHeapOccupancyPercent | 触发并发标记的堆占用率 | 45% |
-XX:G1NewSizePercent | 新生代最小比例 | 5% |
-XX:G1MaxNewSizePercent | 新生代最大比例 | 60% |
-XX:ParallelGCThreads | 并行回收线程数 | CPU 核心数 |
-XX:ConcGCThreads | 并发标记线程数 | CPU 核心数的 1/4 |
六、JVM 调优实战
调优步骤
- 监控 GC 状态:使用 jstat、jconsole 等工具
- 分析 GC 日志:使用 GCViewer、GCEasy 等工具
- 调整堆大小:根据应用需求设置合适的堆大小
- 调整新生代比例:根据对象存活时间调整
- 设置停顿时间目标:根据应用响应时间要求设置
- 优化代码:减少对象创建、避免内存泄漏
GC 日志分析
// 启用 GC 日志
java -Xms2G -Xmx2G -XX:+UseG1GC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log MyApp
日志解读:
2.314: [GC pause (G1 Evacuation Pause) (young), 0.0230250 secs]
[Parallel Time: 20.4 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 2314.0, Avg: 2314.1, Max: 2314.2, Diff: 0.2]
[Ext Root Scanning (ms): Min: 0.1, Avg: 0.2, Max: 0.4, Diff: 0.3, Sum: 1.3]
[Update RS (ms): Min: 0.5, Avg: 0.7, Max: 1.0, Diff: 0.5, Sum: 5.4]
[Process Buffers (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Scan RS (ms): Min: 0.1, Avg: 0.2, Max: 0.3, Diff: 0.2, Sum: 1.2]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Object Copy (ms): Min: 16.6, Avg: 17.0, Max: 17.4, Diff: 0.8, Sum: 135.7]
[Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.6]
[GC Worker End (ms): Min: 2334.4, Avg: 2334.5, Max: 2334.5, Diff: 0.1]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.1 ms]
[Other: 2.5 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 0.6 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.2 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.2 ms]
[Eden: 512.0M(512.0M)->0.0B(512.0M) Survivors: 64.0M->64.0M Heap: 678.5M(2048.0M)->166.5M(2048.0M)]
常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 频繁 Full GC | 老年代空间不足 | 增大堆内存、减少大对象创建 |
| 停顿时间过长 | 堆内存过大或 Region 过多 | 调整 MaxGCPauseMillis、减小堆大小 |
| 内存碎片 | CMS 回收器的问题 | 切换到 G1 回收器 |
| Young GC 频繁 | 新生代空间过小 | 增大新生代比例 |
| Concurrent Mode Failure | CMS 回收速度跟不上 | 切换到 G1 或 ZGC |
七、总结
G1 回收器是现代 JVM 的核心组件:
- 内存布局:基于 Region 分区,支持灵活的内存管理
- 工作流程:新生代收集 + 并发标记 + Mixed GC
- 关键技术:Remembered Set、SATB、停顿预测模型
- 调优参数:控制堆大小、停顿时间、线程数等
调优建议:
- 优先使用 G1 回收器(Java 9+ 默认)
- 根据应用需求设置合理的停顿时间目标
- 监控 GC 状态,及时调整参数
- 优化代码,减少不必要的对象创建
理解 G1 的工作原理和调优方法,对于保证应用程序的性能和稳定性至关重要。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END








请登录后查看评论内容