JVM 垃圾回收
概述
垃圾回收(GC)自动管理 Java 堆内存,回收不再使用的对象。
可回收性判定
可达性分析:从 GC Roots 出发,不可达的对象判定为可回收。
GC Roots 包括:
- 虚拟机栈中引用的对象
- 静态属性引用的对象
- 常量引用的对象
- 本地方法栈中 JNI 引用的对象
- 活跃线程
引用类型:
| 引用类型 | 回收时机 | 用途 |
|---|---|---|
| 强引用 | 永不回收 OOM 才抛异常 | new Object() |
| 软引用 | OOM 前回收 | 缓存(SoftReference) |
| 弱引用 | 下一次 GC | WeakHashMap、ThreadLocal |
| 虚引用 | 任何时候 | 跟踪对象回收(PhantomReference) |
分代收集理论
┌────────────┐
Minor GC │ Eden │ Young
┌──────────────────►│ S0 S1 │
│ └─────┬──────┘
│ │ 年龄增到阈值
│ ▼
│ ┌────────────┐
└───────────────────│ 老年代 │ Old
Full GC │ │
(Major GC) └────────────┘- 新生代:对象优先分配在 Eden,Minor GC 频繁
- 老年代:长期存活的对象,Major GC/Full GC 较慢
- 元空间:类元数据,FGC 触发条件
垃圾回收算法
标记-清除(Mark-Sweep)
标记所有存活对象 → 清除未标记对象- ✅ 简单
- ❌ 产生内存碎片
标记-复制(Mark-Copy)
将存活对象复制到一块空闲区域 → 原区域整块回收- ✅ 无碎片、分配高效(指针碰撞)
- ❌ 浪费一半空间
新生代默认:Eden:S0:S1 = 8:1:1(浪费 10%)
标记-整理(Mark-Compact)
标记所有存活对象 → 向一端移动 → 清理边界外内存- ✅ 无碎片、空间利用率高
- ❌ 移动对象停顿时间长
垃圾回收器
新生代回收器
| 回收器 | 线程 | 算法 | 说明 |
|---|---|---|---|
| Serial | 单线程 | 复制 | Client 模式默认,Stop-The-World |
| ParNew | 多线程 | 复制 | Serial 多线程版,可配合 CMS |
| Parallel Scavenge | 多线程 | 复制 | 关注吞吐量,可自适应调节 |
老年代回收器
| 回收器 | 线程 | 算法 | 说明 |
|---|---|---|---|
| Serial Old | 单线程 | 标记-整理 | Client 模式备用 |
| Parallel Old | 多线程 | 标记-整理 | Parallel Scavenge 的搭档 |
| CMS | 多线程 | 标记-清除 | 低停顿,关注响应时间 |
CMS 详解
初始标记(STW) → 并发标记 → 重新标记(STW) → 并发清除优点:并发、低停顿 缺点:
- 对 CPU 敏感(GC 线程占用资源)
- 无法处理浮动垃圾 → Concurrent Mode Failure
- 内存碎片 → 退化为 Serial Old 整理
参数:
-XX:+UseConcMarkSweepGC 启用 CMS
-XX:CMSInitiatingOccupancyFraction=75 触发阈值
-XX:+UseCMSCompactAtFullCollection FGC 后整理
-XX:CMSFullGCsBeforeCompaction=5 N 次 FGC 后整理一次G1 回收器(JDK 9+ 默认)
将堆划分为 2048 个 Region(1MB~32MB)
优先回收垃圾最多的 Region → Garbage FirstEden | Survivor | Old | Humongous(O > 50% Region)
R R R R R R
R R R R R R
R R R R R RG1 周期:
年轻代 GC (STW, 复制)
↓
并发标记 (初始标记 → Root扫描 → 并发标记 → 重新标记 → 清理)
↓
混合回收 (Mixed GC, 同时回收 Young + Old)关键特性:
- Region 化:堆分为大小相等的 Region
- 停顿预测:
-XX:MaxGCPauseMillis=200控制目标停顿 - Humongous 分配:超大对象直接分配到 Humongous Region
参数:
-XX:+UseG1GC 启用 G1(JDK9+ 默认)
-XX:MaxGCPauseMillis=200 最大停顿目标
-XX:G1HeapRegionSize=4m Region 大小
-XX:InitiatingHeapOccupancyPercent=45 启动并发标记阈值
-XX:G1NewSizePercent=5 年轻代初始大小
-XX:G1HeapWastePercent=5 混合回收触发比例ZGC(JDK 15+ 正式)
着色指针 + 读屏障 + 并发处理
最大停顿 < 1ms,与堆大小无关特性:
- 停顿极低:最大 < 1ms
- 支持超大堆:4TB~16TB
- 每个 Region 分三个视图:已用、已回收、非活动
参数:
-XX:+UseZGC
-Xmx16g
-XX:ZCollectionInterval=120 最大 GC 间隔
-XX:ZAllocationSpikeTolerance=2.0 分配速率容忍Shenandoah(JDK 12+)
与 ZGC 类似,使用转发指针 + 读屏障(而非 ZGC 的着色指针)
回收器对比总表
| 回收器 | 适用场景 | 算法 | 线程 | 停顿 | 备注 |
|---|---|---|---|---|---|
| Serial | 单核/Client | 复制/整理 | 单 | 长 | 默认提供 |
| Parallel | 批处理/吞吐量优先 | 复制/整理 | 多 | 长 | 可设置 -XX:ParallelGCThreads |
| CMS | Web 服务 | 标记清除 | 多 | 短 | JDK14 已废弃 |
| G1 | 通用(默认) | Region 复制 | 多 | 可控 | JDK 9+ 默认 |
| ZGC | 超大堆/极低停顿 | 染色指针 | 多 | <1ms | JDK15+ |
| Shenandoah | 超大堆/极低停顿 | 转发指针 | 多 | <1ms | 需编译 |
GC 调优实战
常用 GC 日志参数
-XX:+PrintGCDetails 打印 GC 详情
-XX:+PrintGCDateStamps 打印时间戳
-XX:+PrintTenuringDistribution 打印年龄分布
-Xloggc:/path/gc-%t.log GC 日志文件(%t=时间戳)
-XX:+UseGCLogFileRotation 日志轮转
-XX:NumberOfGCLogFiles=10 保留 10 个文件
-XX:GCLogFileSize=10M 每个 10MB
-verbose:class 类加载日志典型调优策略
1. 吞吐量优先(Parallel)
-XX:+UseParallelGC -XX:+UseParallelOldGC
-XX:ParallelGCThreads=4
-XX:MaxGCPauseMillis=500 宽松目标
-XX:GCTimeRatio=19 吞吐量 = 1/(1+19) = 95%2. 低停顿(G1)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100
-XX:InitiatingHeapOccupancyPercent=35 提前并发标记
-XX:G1HeapRegionSize=4m3. 超大堆极低停顿(ZGC)
-XX:+UseZGC
-Xmx128g
-XX:ConcGCThreads=8常见 GC 问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 频繁 Full GC | 老年代空间不足 | 增大堆、调整晋升阈值 |
| CMS Concurrent Mode Failure | 老年代填满 | 降低触发阈值、增大堆 |
| G1 Mixed GC 频繁 | 预期停顿过小 | 调大 MaxGCPauseMillis |
| GC 日志过大 | 未配置轮转 | 开启日志轮转 |
| STW 时间过长 | GC 线程不够 | 调大 GC 线程数 |
| 元空间 FGC | 类加载过多 | 增大 MaxMetaspaceSize |