图解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时自动生效
相关推荐
lzj20145 分钟前
芋道源码解读之多租户
后端
异常君6 分钟前
MySQL 事务实现机制:从原理到实践的深度解析
后端·mysql
放情6 分钟前
关于k8s的部署
java·docker·kubernetes
lamdaxu9 分钟前
Kafka源码分析之Producer源码
后端
lamdaxu11 分钟前
Kafka源码分析之Consumer源码
后端
August_._13 分钟前
【JavaWeb】详细讲解 HTTP 协议
java·网络·网络协议·http
zru_960218 分钟前
Java Stream流之GroupBy的用法
java·开发语言
shangxianjiao19 分钟前
Javaweb后端 maven高级 maven聚合
java·maven·springboot·springcloud·聚合
艾露z20 分钟前
Vert.x学习(五)—— SockJS,搭建客户端,与后端服务器进行通信
java·前端·后端·学习·web
Vesan,24 分钟前
C++ static的使用方法及不同作用
java·jvm·c++