图解JVM - 13.垃圾回收器

222 阅读9分钟

1. GC分类与性能指标

1.1 垃圾回收器概述

垃圾回收器是JVM内存管理的核心组件,其本质是通过特定算法实现以下三个核心功能:

  1. 内存区域划分
  2. 对象存活判定
  3. 内存回收策略

1.2 垃圾收集器分类

两种关键分类维度:

  1. 执行方式
    • 串行:单线程执行,全程STW
    • 并行:多线程并行回收
    • 并发:与应用线程交替执行
  2. 内存管理策略
    • 分代式(Generational)
    • 分区式(Region-based)

1.3 评估GC的性能指标

三大核心指标:

  1. 吞吐量(Throughput)
    • 公式:<font style="background-color:rgb(252, 252, 252);">吞吐量 = 用户代码运行时间 / (用户代码运行时间 + GC时间)</font>
    • 生产系统通常要求>90%
  2. 暂停时间(Pause Time)
    • 单次GC导致的应用停顿时间
    • 关键系统要求<200ms
  3. 内存占用(Footprint)
    • JVM堆内存的峰值使用量

指标间的关系

  • 吞吐量 vs 延迟:通常呈反比关系
  • 吞吐量 vs 内存:增大内存可提升吞吐量但增加回收成本

2. 不同的垃圾回收器概述

2.1 垃圾回收器发展史

关键发展阶段:

  1. 单线程时代(1998-2004):Serial收集器主导
  2. 吞吐量优先(2002-2011):Parallel Scavenge崛起
  3. 低延迟革命(2004-2017):CMS和G1的博弈
  4. 新一代回收器(2018至今):ZGC、Shenandoah实现亚毫秒级停顿

2.2 7种经典的垃圾收集器

经典收集器矩阵:

收集器算法线程模式适用场景
Serial复制串行Client模式
ParNew复制并行CMS搭档
Parallel Scavenge复制并行吞吐量优先
Serial Old标记-整理串行Serial后备
Parallel Old标记-整理并行Parallel最佳拍档
CMS标记-清除并发低延迟系统
G1分区算法并发全场景

2.3 分代与收集器关系

关键组合规则:

  1. 新生代与老年代收集器必须兼容
  2. 连线组合关系:
    • Serial/Serial Old
    • ParNew/CMS
    • Parallel Scavenge/Parallel Old
    • G1独立运作

2.4 组合关系示意图

允许的组合方式:

  1. 红色警戒线(禁止组合):
    • ParNew与Parallel Old
    • CMS与Serial Old
  2. 推荐生产组合:
    • Parallel Scavenge + Parallel Old(JDK8默认)
    • ParNew + CMS(已逐步淘汰)
    • G1(JDK9+默认)

2.5 查看默认垃圾收集器

方法一:命令行参数

java -XX:+PrintCommandLineFlags -version

方法二:GC日志分析

// 添加JVM参数
-XX:+PrintGCDetails

示例输出解读

// Parallel收集器特征
PSYoungGen -> Parallel Scavenge
ParOldGen -> Parallel Old

// G1收集器特征
Garbage-First (G1)

不同版本的默认变化

3. Serial回收器:串行回收

核心原理图解

技术特征

  1. 单线程工作模式(GC线程仅使用1个CPU核心)
  2. 全程STW(暂停所有应用线程)
  3. 新生代使用复制算法(Eden + Survivor)
  4. 老年代使用标记-整理算法(Serial Old)

启用参数

-XX:+UseSerialGC

适用场景

  • 客户端模式(Client VM)
  • 内存<100MB的简单应用
  • 嵌入式设备等资源受限环境

优劣对比

4. ParNew回收器:并行回收

并行回收机制

核心改进

  • Serial收集器的多线程版本(并行执行GC)
  • 默认启用线程数 = CPU核心数(可通过<font style="background-color:rgb(252, 252, 252);">-XX:ParallelGCThreads</font>调整)

组合关系

关键参数

-XX:+UseParNewGC
-XX:ParallelGCThreads=4

典型应用

  • JDK7及之前版本与CMS组合使用
  • 服务端多核环境(但逐渐被G1替代)

5. Parallel回收器:吞吐量优先

体系结构

设计哲学

  1. 吞吐量最大化目标:<font style="background-color:rgb(252, 252, 252);">吞吐量 = 用户代码运行时间/(用户代码时间+GC时间)</font>
  2. 自适应调节策略(Auto-tuning):
    • 自动调整堆大小
    • 动态改变晋升阈值

核心参数

-XX:+UseParallelGC
-XX:+UseParallelOldGC
-XX:MaxGCPauseMillis=200  # 最大停顿时间目标
-XX:GCTimeRatio=99        # GC时间占比(1/(1+99))

工作流程

6. CMS回收器:低延迟

四阶段并发收集

关键参数

-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70  # 老年代触发阈值
-XX:+UseCMSCompactAtFullCollection    # FullGC时压缩内存

6.1 CMS优点

6.2 CMS弊端

典型问题处理

  1. 并发模式失败(Concurrent Mode Failure)
    • 解决方案:调低<font style="background-color:rgb(252, 252, 252);">-XX:CMSInitiatingOccupancyFraction</font>
  2. 晋升失败(Promotion Failed)
    • 解决方案:增大Survivor空间或开启<font style="background-color:rgb(252, 252, 252);">-XX:+CMSParallelRemarkEnabled</font>

6.3 参数调优表

参数默认值说明
-XX:+CMSParallelInitialMarkEnabledtrue初始标记多线程加速
-XX:+CMSScavengeBeforeRemarkfalse重新标记前执行Young GC
-XX:+UseCMSInitiatingOccupancyOnlyfalse仅用阈值触发不自动调整

7. G1回收器:区域化分代式

7.1 G1回收器的特点(优势)

核心创新点

  1. Region分区机制:将堆划分为2048个等大小区域(1MB~32MB可调)
  2. 动态角色转换:每个Region可随时作为Eden/Survivor/Old/Humongous使用
  3. 回收优先级:根据回收价值(GC效率)排序Region

7.2 G1的缺点

性能拐点

  • 堆内存<6GB时,CMS可能表现更好
  • 堆内存>6GB时,G1优势显现

7.3 G1参数设置

关键参数表

参数默认值说明
-XX:G1HeapRegionSize根据堆计算设置Region大小(2^N)
-XX:MaxGCPauseMillis200ms最大停顿时间目标
-XX:InitiatingHeapOccupancyPercent45老年代占用阈值触发并发周期
-XX:G1ReservePercent10预留内存防溢出

7.4 G1常见操作步骤

7.5 G1适用场景

典型应用案例

  • 电商大促系统(堆内存32GB,要求GC停顿<200ms)
  • 金融交易系统(需要稳定低延迟)
  • 长期运行的后台服务(避免内存碎片)

7.6 分区Region:化整为零

Humongous对象处理

  1. 超过Region 50%大小的对象
  2. 存放在连续Humongous区
  3. 优先纳入回收范围

7.7 G1垃圾回收过程

7.8 Remembered Set

跨代引用处理

  1. 每个Region维护一个RSet
  2. 使用写屏障(Write Barrier)技术维护
  3. 避免全堆扫描

7.9 年轻代GC过程

阶段耗时占比

7.10 G1回收过程二:并发标记过程

关键机制

  • SATB算法:在标记开始时建立快照,新分配的对象默认标记为存活
  • 并行标记线程:默认线程数 = <font style="background-color:rgb(252, 252, 252);">-XX:ConcGCThreads</font>(建议设置为ParallelGCThreads的1/4)

7.11 G1回收过程三:混合回收

阶段特点

  1. 同时处理年轻代和老年代Region
  2. 多轮回收机制(最多8轮,通过<font style="background-color:rgb(252, 252, 252);">-XX:G1MixedGCCountTarget</font>控制)
  3. 动态调整回收比例:根据<font style="background-color:rgb(252, 252, 252);">-XX:G1HeapWastePercent</font>(默认5%)决定是否停止回收

7.12 G1回收可选的过程四:Full GC

规避Full GC策略

  1. 增加堆内存<font style="background-color:rgb(252, 252, 252);">-Xmx</font>
  2. 降低IHOP阈值<font style="background-color:rgb(252, 252, 252);">-XX:InitiatingHeapOccupancyPercent</font>
  3. 增加预留内存<font style="background-color:rgb(252, 252, 252);">-XX:G1ReservePercent=15</font>
  4. 避免大对象直接进入老年代<font style="background-color:rgb(252, 252, 252);">-XX:G1HeapRegionSize=4M</font>

7.14 G1回收器优化建议

参数调优矩阵

问题现象调优参数预期效果
频繁Mixed GC提高<font style="background-color:rgb(252, 252, 252);">-XX:InitiatingHeapOccupancyPercent</font>减少混合回收触发频率
Remark阶段耗时过长<font style="background-color:rgb(252, 252, 252);">-XX:+CMSScavengeBeforeRemark</font>减少重新标记工作量
并发模式失败增加<font style="background-color:rgb(252, 252, 252);">-XX:G1ReservePercent</font>预留更多内存防止溢出
Young GC时间过长调整<font style="background-color:rgb(252, 252, 252);">-XX:G1NewSizePercent</font>优化年轻代比例

8. 垃圾回收器总结

8.1 7种经典垃圾回收器对比

核心差异总结表

特性\收集器SerialParNewParallelCMSG1
线程模式串行并行并行并发并发
分代策略物理分代物理分代物理分代物理分代逻辑分代
内存碎片处理
最大堆限制4-8G可达TB级
默认使用版本ClientJDK7JDK8逐步淘汰JDK9+

8.2 垃圾回收器组合规则

组合使用原则

  1. 新生代与老年代收集器必须兼容(算法、内存布局)
  2. 启用参数需同时指定:<font style="background-color:rgb(252, 252, 252);">-XX:+UseSerialGC</font>(自动组合Serial+Serial Old)
  3. G1、ZGC等新一代收集器无需组合

8.3 选择收集器的决策树


9. GC日志分析

Minor GC日志示例

[GC pause (G1 Evacuation Pause) (young), 0.0023453 secs]
   [Parallel Time: 1.8 ms, GC Workers: 8]
      [GC Worker Start (ms): Min: 100.1, Avg: 100.2, Max: 100.3]
      [Ext Root Scanning (ms): Min: 0.1, Avg: 0.3, Max: 0.5]
      [Update RS (ms): Min: 0.0, Avg: 0.1, Max: 0.2]
         [Processed Buffers: Min: 0, Avg: 1.2, Max: 3]
      [Scan RS (ms): Min: 0.0, Avg: 0.1, Max: 0.2]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0]
      [Object Copy (ms): Min: 0.8, Avg: 1.0, Max: 1.2]
      [Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.2]
   [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0]
   [GC Worker Total (ms): Min: 1.6, Avg: 1.7, Max: 1.8]

关键字段解析

  • <font style="background-color:rgb(252, 252, 252);">G1 Evacuation Pause</font>:G1的年轻代回收阶段
  • <font style="background-color:rgb(252, 252, 252);">GC Workers: 8</font>:并行GC线程数
  • <font style="background-color:rgb(252, 252, 252);">Object Copy</font>:对象复制耗时(主要性能指标)

Full GC日志示例

[Full GC (Allocation Failure) 
  [PSYoungGen: 1024K->0K(2048K)] 
  [ParOldGen: 3072K->4096K(4096K)] 
  4096K->4096K(6144K), 
  [Metaspace: 256K->256K(1024K)], 
  0.123456 secs]

问题诊断点

  • <font style="background-color:rgb(252, 252, 252);">Allocation Failure</font>:内存分配失败触发
  • ParOldGen回收后内存增加:内存泄漏迹象
  • Metaspace数据不变:可能类加载器未释放

10. 垃圾回收器新发展

10.1 ZGC核心特性

版本演进

  • JDK11:实验功能
  • JDK15:生产可用
  • JDK17:支持分代收集(ZGC Generational)

10.2 Shenandoah GC

核心创新

  1. 并发压缩算法
  2. 连接矩阵替代RSet
  3. 增量回收机制

与G1对比

10.3 AliGC(阿里优化版)

商业版增强功能

  1. 多租户隔离回收
  2. 弹性堆内存管理
  3. 基于AI的预测回收

11. 常见问题与解决方案

问题排查表

现象可能原因解决方案
频繁Full GC内存泄漏/过小堆堆dump分析/增加-Xmx
Young GC时间过长过早提升/大对象调整SurvivorRatio/检查大对象
CMS并发模式失败回收速度跟不上分配速度降低触发阈值/增加并发线程
G1混合回收效率低Region存活数据过多调整IHOP阈值/减少Humongous分配

12. 高频面试问题与解答

Q1:CMS和G1的主要区别是什么?

答案

  • 内存布局:CMS物理分代 vs G1逻辑分代
  • 回收算法:CMS标记-清除 vs G1复制+整理
  • 停顿目标:CMS关注低延迟 vs G1可预测停顿
  • 堆内存:CMS适合中等堆 vs G1适合大堆

Q2:如何选择垃圾回收器?

决策流程

  1. 堆大小:<6GB选CMS,>6GB选G1
  2. 延迟要求:要求亚秒级选ZGC/Shenandoah
  3. JDK版本:JDK8用Parallel/CMS,JDK11+用G1/ZGC

Q3:-XX:+UseCompressedOops的作用?

解析

  • 启用压缩指针(32位指针存64位地址)
  • 节省内存(提升约20%)
  • 堆内存<32GB时自动生效