图解JVM - 13.垃圾回收器

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 查看默认垃圾收集器

方法一:命令行参数

bash 复制代码
java -XX:+PrintCommandLineFlags -version

方法二:GC日志分析

java 复制代码
// 添加JVM参数
-XX:+PrintGCDetails

示例输出解读:

plain 复制代码
// 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)

启用参数:

bash 复制代码
-XX:+UseSerialGC

适用场景:

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

优劣对比:

4. ParNew回收器:并行回收

并行回收机制

核心改进:

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

组合关系:

关键参数:

bash 复制代码
-XX:+UseParNewGC
-XX:ParallelGCThreads=4

典型应用:

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

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

体系结构

设计哲学:

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

核心参数:

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

工作流程:

6. CMS回收器:低延迟

四阶段并发收集

关键参数:

bash 复制代码
-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:+CMSParallelInitialMarkEnabled true 初始标记多线程加速
-XX:+CMSScavengeBeforeRemark false 重新标记前执行Young GC
-XX:+UseCMSInitiatingOccupancyOnly false 仅用阈值触发不自动调整

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:MaxGCPauseMillis 200ms 最大停顿时间目标
-XX:InitiatingHeapOccupancyPercent 45 老年代占用阈值触发并发周期
-XX:G1ReservePercent 10 预留内存防溢出

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种经典垃圾回收器对比

核心差异总结表:

特性\收集器 Serial ParNew Parallel CMS G1
线程模式 串行 并行 并行 并发 并发
分代策略 物理分代 物理分代 物理分代 物理分代 逻辑分代
内存碎片处理 无 无 无 有 无
最大堆限制 无 无 无 4-8G 可达TB级
默认使用版本 Client JDK7 JDK8 逐步淘汰 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日志示例

plain 复制代码
[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日志示例

plain 复制代码
[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时自动生效
相关推荐
Pniubi5 小时前
模拟一个充值送token怎么保证幂等?一致性?原子性?
java
企业数字化笔记5 小时前
多租户配额怎么控制?用量口径、并发预留与超额处理的实现方法
java·数据库·spring boot
IT_陈寒5 小时前
React组件莫名重渲染?你可能忘了这个小细节
前端·人工智能·后端
巴勒个啦5 小时前
Prompt Engineering 实战:5 个让 AI 生成生产级业务代码的工程模式
java·前端
用户9888321871745 小时前
给 .NET 网络调用设定“预算”:用 CancellationTokenSource.CancelAfter 处理超时取消
后端
清风儿3295 小时前
一块不会说话的电表,把我们折腾了半个月
后端
倚楼听风语5 小时前
Kotlin 协程与 suspend 原理详解
java·算法·kotlin
预知同行5 小时前
深入解析 MoE 混合专家模型:从 Router 到 All-to-All 的架构全景与生产实践
后端·架构
upala5 小时前
【仓颉语言入门 · 第二篇 第 8 课】
后端
weiwin1236 小时前
MAF 入门(8):多模态实战——让 Agent 看懂图片、处理混合内容
人工智能·后端