深入理解JVM垃圾收集器:垃圾收集器
目录
前言
1. 如何判断对象"已死"?
1.1 引用计数算法(Java未采用)
1.2 可达性分析算法(Java采用)
1.3 Java中的四种引用类型
2. 垃圾收集算法
2.1 标记-清除算法(Mark-Sweep)
2.2 标记-复制算法(Copying)
2.3 标记-整理算法(Mark-Compact)
2.4 分代收集算法(Generational Collection)
3. 经典垃圾收集器
3.1 Serial收集器
3.2 ParNew收集器
3.3 Parallel Scavenge收集器
3.4 Serial Old收集器
3.5 Parallel Old收集器
3.6 CMS收集器(Concurrent Mark Sweep)
3.7 G1收集器(Garbage-First)
4. 新一代垃圾收集器
4.1 ZGC(Z Garbage Collector)
4.2 Shenandoah
5. 如何选择垃圾收集器?
6. 实战调优建议
6.1 通用参数配置
6.2 G1调优示例
前言
作为Java开发者,我们几乎每天都在与JVM打交道,但你是否真正了解JVM是如何自动管理内存、回收垃圾对象的?本文将带你深入探讨JVM垃圾收集器的核心知识,帮助你编写出更高效、更稳定的Java应用。
1. 如何判断对象"已死"?
在了解垃圾收集器之前,我们首先要明白JVM如何判断哪些对象可以被回收。
1.1 引用计数算法(Java未采用)
早期的一种简单策略:每个对象有一个引用计数器,被引用时+1,引用失效时-1。虽然简单高效,但无法解决循环引用问题,因此Java并未采用。
1.2 可达性分析算法(Java采用)
这是JVM使用的算法:通过一系列称为"GC Roots"的对象作为起点,向下搜索,搜索走过的路径称为"引用链"。当一个对象到GC Roots没有任何引用链相连时,则证明此对象不可用。
GC Roots包括:
- 虚拟机栈中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI引用的对象
1.3 Java中的四种引用类型
Java根据引用强度不同,提供了灵活的引用方式:
引用类型 | 回收时机 | 使用场景 |
强引用 | 永不回收(除非显式置null) | 普通对象创建 |
软引用 | 内存不足时回收 | 实现内存敏感缓存 |
弱引用 | 下次GC时回收 | 避免内存泄漏(如WeakHashMap) |
虚引用 | 无法通过get()获取对象,仅用于跟踪对象回收 | 管理堆外内存 |
2. 垃圾收集算法
垃圾收集器是基于特定算法实现的,主要有以下三种基础算法:
2.1 标记-清除算法(Mark-Sweep)
最基础的算法,分为"标记"和"清除"两个阶段:
- 优点:实现简单
- 缺点:产生内存碎片,效率不高
2.2 标记-复制算法(Copying)
将内存分为两块,每次只使用一块:
- 优点:没有碎片,高效
- 缺点:内存利用率仅50%
- 应用:主要用于新生代(HotSpot采用8:1:1的比例优化)
2.3 标记-整理算法(Mark-Compact)
标记过程与"标记-清除"一样,但后续步骤不是直接清理,而是让所有存活对象向一端移动:
- 优点:没有碎片,内存利用率高
- 缺点:移动对象成本高
- 应用:主要用于老年代
2.4 分代收集算法(Generational Collection)
现代商用虚拟机大都采用此算法,根据对象存活周期不同将内存划分为几块:
- 新生代:对象朝生夕死,采用复制算法
- 老年代:对象存活率高,采用标记-清除或标记-整理算法
3. 经典垃圾收集器
JVM提供了多种垃圾收集器,适用于不同场景:
3.1 Serial收集器
特点:单线程,Stop-The-World(STW)
适用场景:客户端模式,小内存应用
配置参数:-XX:+UseSerialGC
3.2 ParNew收集器
特点:Serial的多线程版本
适用场景:与CMS配合使用
配置参数:-XX:+UseParNewGC
3.3 Parallel Scavenge收集器
特点:吞吐量优先,多线程
适用场景:后台运算,不需要太多交互
配置参数:-XX:+UseParallelGC
3.4 Serial Old收集器
特点:Serial的老年代版本,单线程,标记-整理算法
配置参数:-XX:+UseSerialGC
3.5 Parallel Old收集器
特点:Parallel Scavenge的老年代版本,多线程,标记-整理算法
配置参数:-XX:+UseParallelOldGC
3.6 CMS收集器(Concurrent Mark Sweep)
特点:以获取最短回收停顿时间为目标,并发收集
工作流程:
- 初始标记(STW)
- 并发标记
- 重新标记(STW)
- 并发清除
优点:低停顿
缺点:对CPU资源敏感,无法处理浮动垃圾,产生碎片
配置参数:-XX:+UseConcMarkSweepGC
3.7 G1收集器(Garbage-First)
特点:面向服务端应用,兼顾吞吐量和停顿时间
创新点:将堆划分为多个Region,优先回收价值最大的Region
工作流程:
- 初始标记(STW)
- 并发标记
- 最终标记(STW)
- 筛选回收(STW)
配置参数:-XX:+UseG1GC
4. 新一代垃圾收集器
4.1 ZGC(Z Garbage Collector)
特点:低延迟,支持TB级堆内存,停顿时间不超过10ms
适用场景:超大堆内存应用
配置参数:-XX:+UseZGC
(JDK15+)
5. 如何选择垃圾收集器?
选择收集器需要考虑应用特点:
场景 | 推荐收集器 | 参数配置 |
单核CPU或小内存客户端 | Serial |
|
多核CPU追求高吞吐 | Parallel Scavenge/Old |
|
多核CPU追求低延迟(JDK8) | CMS |
|
大堆内存,平衡吞吐和延迟 | G1 |
|
超大堆内存,极致低延迟 | ZGC |
|
6. 实战调优建议
6.1 通用参数配置
# 设置堆大小
-Xms4g -Xmx4g# 设置年轻代大小
-Xmn2g# 设置线程栈大小
-Xss256k# 开启GC日志
-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
6.2 G1调优示例
java -Xms4g -Xmx4g -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \ # 目标最大停顿时间
-XX:InitiatingHeapOccupancyPercent=45 \ # 触发并发标记的堆使用率
-XX:ConcGCThreads=4 \ # 并发GC线程数
-XX:G1ReservePercent=10 \ # 保留内存比例
-jar your-application.jar