7 种回收器总结与调优建议
七种回收器全景对比
| 收集器 | 年轻代 | 老年代 | 算法 | 停顿 | 吞吐量 | JDK 版本 | 默认 |
|---|---|---|---|---|---|---|---|
| Serial | Serial | Serial Old | 复制 / 标记-压缩 | 很长 | 低 | 1.3+ | - |
| ParNew | ParNew | CMS | 复制 / 标记-清除 | 较短 | 中 | 1.4+ | CMS 默认 |
| Throughput | PS | PO | 复制 / 标记-压缩 | 长 | 最高 | 1.4+ | - |
| CMS | ParNew | CMS | 复制 / 标记-清除 | 短 | 中 | 1.5+ | JDK 14 移除 |
| G1 | G1 | G1 | 复制 / 标记-清除 | 可预测 | 中 | 7+ | JDK 9+ 默认 |
| ZGC | ZGC | ZGC | 着色指针 + 并发 | < 1ms | 中 | 11+ | JDK 21 默认 |
| Shenandoah | Sh. | Sh. | 转发指针 | < 10ms | 中 | 12+ | - |
收集器组合图
┌─────────────────────────────────────────────────────────┐
│ JVM 堆 │
│ │
│ 年轻代 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Serial | ParNew | PS Scavenge | G1 | ZGC | Sh. │ │
│ └──────────────────────────────────────────────────┘ │
│ │ 晋升 │
│ 老年代 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Serial Old | CMS | PO | G1 | ZGC | Shenandoah │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘选型决策树
需要选择 GC 收集器?
│
├── 是否需要低延迟?(Web 服务、实时系统)
│ │
│ ├── 是否需要 < 1ms 停顿?
│ │ │
│ │ ├── 是 → ZGC(JDK 11+)
│ │ │
│ │ └── 否 → G1(JDK 9+ 默认)
│ │
│ └── 否(批处理、离线计算)
│ │
│ └── Throughput/Parallel GC
│
├── 内存很小?(< 1GB)
│ │
│ └── Serial
│
├── 使用 JDK 8?
│ │
│ └── Throughput GC(JDK 8 推荐)
│
└── 不想调优?
│
└── G1(JDK 9+ 默认,开箱即用)调优建议
通用调优原则
| 原则 | 说明 |
|---|---|
| 不要过早优化 | 先用默认配置(JDK 9+ 默认 G1) |
| 基于数据调优 | 通过 GC 日志和监控数据做决策 |
| 一次只改一个参数 | 便于定位问题 |
| 设置合理的期望 | GC 不是万能的,停顿和吞吐量是对立的 |
调优步骤
1. 开启 GC 日志
└─ java -Xlog:gc*:file=gc.log MyApp
2. 运行一段时间,收集数据
└─ jstat -gcutil <pid> 1000
3. 分析数据
├─ 吞吐量是否满足?
├─ 停顿时间是否可接受?
└─ 内存使用是否合理?
4. 针对性调优
├─ 吞吐量不够 → 增大堆 / 使用 Throughput GC
├─ 停顿太长 → 使用 G1 / ZGC
└─ 内存泄漏 → 排查泄漏点
5. 重复 2-4 直到满意Throughput GC 调优
bash
# 目标:高吞吐量
java -Xms4g -Xmx4g \
-XX:+UseParallelGC \
-XX:+UseParallelOldGC \
-XX:GCTimeRatio=19 \ # 目标吞吐量 95%
-XX:MaxGCPauseMillis=500 \ # 最大停顿 500ms
-XX:+UseAdaptiveSizePolicy \ # 自动调节
MyAppG1 调优
bash
# 目标:可预测停顿
java -Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \ # 目标停顿 200ms
-XX:InitiatingHeapOccupancyPercent=45 \
MyAppZGC 调优
bash
# 目标:亚毫秒停顿
java -Xms16g -Xmx16g \
-XX:+UseZGC \
-XX:MaxGCPauseMillis=1 \
MyAppGC 参数速查表
| 场景 | 推荐参数 |
|---|---|
| 标准 Web 服务 | -XX:+UseG1GC -XX:MaxGCPauseMillis=200 |
| 低延迟金融系统 | -XX:+UseZGC -XX:MaxGCPauseMillis=1 |
| 批处理/Hadoop 风格 | -XX:+UseParallelGC -XX:GCTimeRatio=19 |
| 大内存(> 32GB) | -XX:+UseZGC -XX:MaxGCPauseMillis=5 |
| 容器/K8s | -XX:+UseContainerSupport -XX:+UseG1GC |
| JVM 基准测试 | -XX:+UseEpsilonGC |
常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| FullGC 频繁 | 对象晋升太快 | 增大 Survivor 区,降低晋升阈值 |
| 停顿时间过长 | GC 选择不当 | 切换到 G1 / ZGC |
| 内存持续增长 | 内存泄漏 | 排查泄漏点(dump + MAT) |
| 吞吐量不足 | GC 太过频繁 | 增大堆,使用 Parallel GC |
| 元空间满 | 类加载过多 | 增大 -XX:MaxMetaspaceSize |
| 直接内存满 | NIO 使用过多 | 增大 -XX:MaxDirectMemorySize |
JDK 版本与 GC 推荐
| JDK 版本 | 推荐 GC |
|---|---|
| JDK 8 | Throughput GC(UseParallelGC) |
| JDK 9~10 | G1 |
| JDK 11~20 | ZGC(低延迟)/ G1 |
| JDK 21+ | ZGC(默认) |
本节小结
GC 调优的本质是取舍:
吞吐量和停顿时间是对立的:
高吞吐量 → 每次 GC 多做工作 → 停顿时间更长
低停顿 → 每次 GC 少做工作 → 吞吐量降低
没有完美的 GC,只有适合的 GC:
批处理 → Throughput GC
Web 服务 → G1 / ZGC
金融交易 → ZGC
默认选择 → G1(JDK 9~20)/ ZGC(JDK 21+)记住:不要为了调优而调优。先用 G1(JDK 9+)或 ZGC(JDK 11+),只有在 GC 日志告诉你有问题时,才针对性地调优。
到这里,「垃圾回收器(实战)」全部完成。接下来进入最后一部分——GC 日志分析。
