🗑️ 垃圾收集器详解
深入学习JVM垃圾收集器的工作原理、性能特点和调优策略,掌握不同场景下的最佳选择
学习目标
- 理解垃圾收集器的分类和工作原理
- 掌握Serial、Parallel、CMS、G1等收集器的特点
- 学会根据应用场景选择合适的垃圾收集器
- 掌握垃圾收集器的调优参数和最佳实践
- 了解新一代收集器ZGC和Shenandoah的特性
垃圾收集器概述
垃圾收集器(Garbage Collector,GC)是JVM内存管理的核心组件,负责自动回收不再使用的对象所占用的内存空间。不同的垃圾收集器有着不同的工作原理、性能特点和适用场景。
选择合适的垃圾收集器是JVM调优的关键决策之一,需要根据应用的特点、性能要求和硬件环境来综合考虑。
垃圾收集器的分类
- 新生代收集器:Serial、ParNew、Parallel Scavenge
- 老年代收集器:Serial Old、Parallel Old、CMS
- 整堆收集器:G1、ZGC、Shenandoah
- 串行收集器:单线程执行,适合小型应用
- 并行收集器:多线程执行,适合吞吐量优先场景
- 并发收集器:与应用线程并发执行,适合低延迟场景
- Stop-The-World:需要暂停所有应用线程
- 并发收集:大部分时间与应用线程并发执行
- 增量收集:将收集工作分解为多个小步骤
Serial收集器
Serial收集器是最基本、历史最悠久的垃圾收集器。它是一个单线程收集器,在进行垃圾收集时必须暂停所有工作线程,直到收集结束。
工作原理
- 单线程执行:只使用一个线程进行垃圾收集
- Stop-The-World:收集期间暂停所有应用线程
- 复制算法:新生代使用复制算法
- 标记-整理算法:老年代使用标记-整理算法
适用场景
配置参数
# 使用Serial收集器(新生代)
-XX:+UseSerialGC
# 设置新生代大小
-Xmn128m
# 设置Eden与Survivor比例
-XX:SurvivorRatio=8
# 设置对象进入老年代的年龄阈值
-XX:MaxTenuringThreshold=15
Parallel收集器
Parallel收集器是JDK 1.4.2引入的多线程垃圾收集器,也被称为"吞吐量优先"收集器。它是Server模式下的默认收集器。
主要特点
- 多线程并行:使用多个线程并行进行垃圾收集
- 吞吐量优先:关注整体的吞吐量而非单次停顿时间
- 自适应调节:可以自动调节堆大小、停顿时间等参数
- 适合批处理:特别适合后台运算而不需要太多交互的任务
收集器组合
配置参数
# 使用Parallel收集器
-XX:+UseParallelGC
-XX:+UseParallelOldGC
# 设置并行收集线程数
-XX:ParallelGCThreads=4
# 设置最大停顿时间目标(毫秒)
-XX:MaxGCPauseMillis=200
# 设置吞吐量目标(垃圾收集时间占总时间的比例)
-XX:GCTimeRatio=19
# 启用自适应大小策略
-XX:+UseAdaptiveSizePolicy
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它在垃圾收集时能够与应用线程并发执行,大大减少了停顿时间。
工作流程
优缺点分析
- 低延迟:并发收集,停顿时间短
- 响应性好:适合对响应时间敏感的应用
- 并发执行:大部分收集工作与应用线程并发
- CPU敏感:并发阶段会占用CPU资源
- 浮动垃圾:并发清除阶段产生的垃圾无法处理
- 内存碎片:使用标记-清除算法,会产生内存碎片
- Concurrent Mode Failure:可能导致Full GC
配置参数
# 使用CMS收集器
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
# 设置CMS触发阈值
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
# 启用类卸载
-XX:+CMSClassUnloadingEnabled
# 设置CMS并发线程数
-XX:ConcGCThreads=2
# 启用增量模式(已废弃)
# -XX:+CMSIncrementalMode
G1收集器
G1(Garbage First)收集器是JDK 7引入的低延迟垃圾收集器,从JDK 9开始成为默认收集器。它将堆内存划分为多个大小相等的Region,能够实现可预测的停顿时间。
核心特性
收集模式
- Young GC:收集所有年轻代Region
- Mixed GC:收集年轻代和部分老年代Region
- Full GC:收集整个堆(应该避免)
配置参数
# 使用G1收集器
-XX:+UseG1GC
# 设置期望的停顿时间目标(毫秒)
-XX:MaxGCPauseMillis=200
# 设置Region大小(1MB-32MB,必须是2的幂)
-XX:G1HeapRegionSize=16m
# 设置新生代占堆的最小比例
-XX:G1NewSizePercent=20
# 设置新生代占堆的最大比例
-XX:G1MaxNewSizePercent=40
# 设置触发Mixed GC的老年代占用阈值
-XX:G1MixedGCLiveThresholdPercent=85
新一代收集器
随着硬件技术的发展和应用需求的变化,JVM引入了新一代的垃圾收集器,如ZGC和Shenandoah,它们能够在大堆内存下实现极低的停顿时间。
ZGC收集器
Shenandoah收集器
配置示例
# ZGC配置
-XX:+UseZGC
-XX:+UnlockExperimentalVMOptions # JDK 11-14需要
# Shenandoah配置
-XX:+UseShenandoahGC
-XX:+UnlockExperimentalVMOptions # JDK 12-14需要
# 通用配置
-Xmx8g
-XX:+UseTransparentHugePages
收集器选择指南
选择合适的垃圾收集器需要考虑应用的特点、性能要求、硬件环境等多个因素。以下是一些选择建议:
选择决策树
- 堆内存 < 100MB:Serial GC
- 单核CPU:Serial GC
- 对停顿时间不敏感:Serial GC
- 吞吐量优先:Parallel GC
- 延迟敏感:G1 GC
- 大堆内存(>6GB):G1 GC
- 极低延迟要求:ZGC/Shenandoah
- 实时系统:ZGC/Shenandoah
- 大数据处理:Parallel GC
- Web应用:G1 GC
- 微服务:G1 GC
性能对比
- 吞吐量:Parallel GC > G1 GC > CMS GC > ZGC/Shenandoah
- 延迟:ZGC/Shenandoah < G1 GC < CMS GC < Parallel GC
- 内存占用:Serial GC < Parallel GC < CMS GC < G1 GC < ZGC/Shenandoah
- CPU占用:Serial GC < Parallel GC < G1 GC < CMS GC < ZGC/Shenandoah
调优最佳实践
垃圾收集器的调优是一个持续的过程,需要根据应用的实际运行情况进行监控和调整。
调优步骤
监控工具
# 启用GC日志
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCApplicationStoppedTime
-Xloggc:gc.log
# JDK 9+的日志配置
-Xlog:gc*:gc.log:time,tags
# 常用监控工具
# 1. jstat - JVM统计信息
jstat -gc 1s
# 2. GCViewer - GC日志分析
# 3. GCPlot.com - 在线GC日志分析
# 4. VisualVM - 可视化监控工具
实战案例
通过实际的调优案例来理解垃圾收集器的选择和参数调整过程。
案例分析
- 基准测试:建立性能基准,记录调优前的关键指标
- 逐步调整:一次只调整一个参数,观察效果
- 压力测试:在接近生产环境的负载下进行测试
- 长期监控:关注长期运行的稳定性和性能表现