1. G1垃圾回收器概述
G1(Garbage-First)是JDK 7中引入的服务器端垃圾回收器,设计初衷是为了替代CMS回收器。与传统的分代回收器不同,G1采用了一种全新的内存布局方式——将堆内存划分为多个大小相等的Region(区域),每个Region可以是Eden区、Survivor区或Old区。这种设计使得G1能够更灵活地管理内存,避免全堆回收带来的长时间停顿。
G1的核心思想是"优先回收垃圾最多的区域"(Garbage-First),这也是它名称的由来。通过可预测的停顿时间模型,G1能够在用户指定的停顿时间目标内(通过-XX:MaxGCPauseMillis参数设置),尽可能提高吞吐量。在实际应用中,G1特别适合大内存(6GB以上)和多核处理器的场景。
2. G1内存布局与核心概念
2.1 Region分区设计
G1将堆划分为多个大小相等的Region,默认情况下Region大小由堆总大小决定,范围在1MB到32MB之间。可以通过-XX:G1HeapRegionSize参数显式设置Region大小。每个Region在运行时会根据用途被标记为以下几种类型:
- Eden Region:存放新创建的对象
- Survivor Region:存放年轻代存活的对象
- Old Region:存放长期存活的对象
- Humongous Region:存放巨型对象(大小超过Region 50%的对象)
- Available Region:未分配的空闲区域
这种设计打破了传统分代垃圾回收器的物理隔离,使得内存管理更加灵活。G1可以根据回收效率动态调整各代的大小,这是它与传统回收器的本质区别。
2.2 记忆集与卡表
由于G1不采用物理分代,对象引用可能跨Region存在。为了高效跟踪跨Region引用,G1使用了记忆集(Remembered Set)和卡表(Card Table)机制:
- 每个Region都有一个记忆集,记录其他Region对本Region的引用
- 卡表将堆内存划分为512字节的卡片(Card),用于标记脏卡片(被修改的内存区域)
- 写屏障(Write Barrier)技术用于维护记忆集和卡表的正确性
这种设计虽然增加了内存开销(通常占堆的10%-20%),但大大减少了垃圾回收时的扫描范围,提高了回收效率。
3. G1回收过程详解
3.1 年轻代回收(Young GC)
年轻代回收是G1中最频繁发生的回收行为,主要回收Eden区和Survivor区。其过程大致如下:
- 根扫描:从GC Roots开始,标记所有可达对象
- 处理记忆集:扫描记忆集记录的跨Region引用
- 对象复制:将存活对象复制到新的Survivor区或晋升到Old区
- 引用处理:更新引用关系,处理软引用、弱引用等特殊引用
年轻代回收是"Stop-The-World"的,但通常停顿时间较短。可以通过-XX:MaxGCPauseMillis参数调整期望的最大停顿时间,G1会根据这个目标动态调整年轻代大小。
3.2 混合回收(Mixed GC)
当堆使用率达到一定阈值(默认45%,可通过-XX:InitiatingHeapOccupancyPercent调整)时,G1会启动混合回收。混合回收不仅会回收年轻代,还会选择部分Old Region进行回收。选择回收Region的标准基于回收效率预测:
- 并发标记阶段:与应用程序并发执行,标记所有存活对象
- 最终标记阶段:短暂停顿,完成标记工作
- 筛选回收阶段:根据回收价值排序Region,选择最"有利可图"的Region回收
混合回收通过优先回收垃圾最多的Region,实现了更高的回收效率。这也是G1名称的由来——Garbage-First。
3.3 全堆回收(Full GC)
当G1无法满足回收需求时(如并发标记失败、晋升失败等),会退化为串行Full GC。这是G1最不希望发生的情况,会导致长时间的停顿。为避免Full GC,需要合理配置以下参数:
- -XX:ConcGCThreads:并发GC线程数
- -XX:G1ReservePercent:预留内存百分比
- -XX:G1HeapWastePercent:允许的堆浪费比例
4. G1关键参数调优
4.1 基础参数配置
- -XX:+UseG1GC:启用G1垃圾回收器
- -XX:MaxGCPauseMillis=200:设置目标最大停顿时间(毫秒)
- -XX:G1HeapRegionSize=16m:设置Region大小
- -XX:ParallelGCThreads=8:设置并行GC线程数
4.2 高级调优参数
- -XX:InitiatingHeapOccupancyPercent=45:触发并发标记的堆占用率
- -XX:G1NewSizePercent=5:年轻代最小占比
- -XX:G1MaxNewSizePercent=60:年轻代最大占比
- -XX:G1MixedGCLiveThresholdPercent=85:混合回收的Region存活对象阈值
- -XX:G1HeapWastePercent=5:允许的堆浪费比例
4.3 监控与诊断参数
- -XX:+PrintGCDetails:打印详细GC日志
- -XX:+PrintGCDateStamps:打印GC时间戳
- -XX:+PrintAdaptiveSizePolicy:打印自适应大小策略调整
- -XX:+PrintTenuringDistribution:打印对象年龄分布
- -XX:+PrintReferenceGC:打印引用处理信息
5. G1常见问题与解决方案
5.1 并发模式失败
当G1无法在堆填满前完成并发标记时,会发生并发模式失败,导致Full GC。解决方案包括:
- 增加-XX:ConcGCThreads值,提高并发标记线程数
- 降低-XX:InitiatingHeapOccupancyPercent值,提前启动并发标记
- 增加堆大小或减少对象分配速率
5.2 晋升失败
当年轻代回收时Old区空间不足,会导致晋升失败。可以采取以下措施:
- 增加-XX:G1ReservePercent值,预留更多内存
- 调整-XX:G1MaxNewSizePercent,控制年轻代最大大小
- 检查内存泄漏,减少不必要的对象保留
5.3 巨型对象分配问题
巨型对象(Humongous对象)会占用整个Region,可能导致以下问题:
-
频繁的Humongous分配会导致GC效率下降
-
不连续的Humongous Region会导致堆碎片化
解决方案包括: -
优化数据结构,避免创建过大的对象
-
增加Region大小(-XX:G1HeapRegionSize)
-
监控Humongous分配(通过GC日志或JVM工具)
6. G1性能优化实践
6.1 内存分配优化
G1对内存分配速度非常敏感,可以通过以下方式优化:
- 使用TLAB(线程本地分配缓冲区),减少分配竞争
- 避免过度同步,减少锁竞争
- 优化对象大小,使其更适合缓存行
6.2 并发标记优化
并发标记阶段对应用性能影响较大,可以采取以下措施:
- 在低峰期手动触发GC(System.gc())
- 避免在标记阶段创建大量新对象
- 优化引用关系,减少跨Region引用
6.3 停顿时间控制
要实现稳定的低停顿时间,需要注意:
- 设置合理的-XX:MaxGCPauseMillis值(通常100-200ms)
- 监控GC日志,分析停顿时间分布
- 避免突然的对象分配高峰
7. G1与其他回收器对比
7.1 G1 vs CMS
- CMS采用传统的分代设计,而G1使用Region分区
- CMS只回收老年代,G1可以同时管理年轻代和老年代
- CMS会产生内存碎片,G1通过压缩避免碎片
- G1提供可预测的停顿时间模型,CMS没有
7.2 G1 vs Parallel GC
- Parallel GC追求高吞吐量,G1追求低停顿
- Parallel GC采用分代设计,G1使用Region分区
- Parallel GC适合中小堆,G1适合大堆内存
- Parallel GC停顿时间不可控,G1可预测停顿
7.3 G1 vs ZGC/Shenandoah
- ZGC和Shenandoah是新一代低延迟回收器
- 它们的目标停顿时间比G1更短(<10ms)
- 但G1在JDK 8及更早版本中更成熟稳定
- G1的内存开销通常比ZGC/Shenandoah小
8. G1最佳实践
8.1 应用场景选择
G1最适合以下场景:
- 堆大小超过6GB的应用
- 需要平衡吞吐量和停顿时间的应用
- 多核处理器环境
- 对象分配和晋升速率适中的应用
8.2 参数配置建议
对于典型的生产环境,建议配置:
bash复制-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-XX:ParallelGCThreads=<CPU核心数>
-XX:ConcGCThreads=<CPU核心数的1/4>
-XX:G1HeapRegionSize=16m
-XX:InitiatingHeapOccupancyPercent=30
-XX:G1ReservePercent=10
8.3 监控与调优流程
- 启用详细GC日志记录
- 使用工具(如GCViewer)分析日志
- 识别问题(长停顿、高频率等)
- 针对性调整参数
- 验证改进效果
- 持续监控和优化
在实际使用G1的过程中,我发现最重要的是理解应用的内存分配模式。通过长期监控GC行为,可以找到最适合特定应用的参数组合。例如,对于突发性负载较高的应用,可能需要设置较低的InitiatingHeapOccupancyPercent值,以便提前启动并发标记。