Parallel/Parallel Old 回收器
高吞吐量的代表
Parallel GC(Throughput GC)是 JVM 中吞吐量最高的收集器组合:年轻代用 Parallel Scavenge,老年代用 Parallel Old。它专门为高吞吐量场景设计,适合批处理和离线计算。
Parallel Scavenge vs ParNew
很多人把 Parallel Scavenge 和 ParNew 混淆——它们确实非常相似(都是多线程年轻代收集器,都用复制算法),但关键区别在于优化目标不同:
| 维度 | Parallel Scavenge | ParNew |
|---|---|---|
| 设计目标 | 高吞吐量 | 尽量缩短停顿时间 |
| 自适应调节 | 支持(自动调节 Survivor、晋升阈值) | 不支持 |
| 可组合 | Parallel Old(Throughput 组合) | CMS |
| 吞吐量 | 更高(专门优化) | 较低 |
| 停顿时间 | 较长 | 较短 |
Parallel GC 的工作原理
年轻代:Parallel Scavenge(吞吐量优先的复制算法)
年轻代收集(Parallel Scavenge):
┌──────────────────────────────────────────────┐
│ │
│ 线程1 ──▶ Eden 区域1 │
│ 线程2 ──▶ Eden 区域2 │
│ 线程3 ──▶ Eden 区域3 │
│ 线程4 ──▶ Eden 区域4 │
│ │
│ N 个线程并行扫描、复制存活对象 │
│ │
│ 吞吐量目标:吞吐量优先,而非最短停顿 │
│ │
└──────────────────────────────────────────────┘老年代:Parallel Old(吞吐量优先的标记-压缩)
老年代收集(Parallel Old):
┌──────────────────────────────────────────────┐
│ │
│ 线程1 ──▶ 老年代区域1 ──▶ 标记 │
│ 线程2 ──▶ 老年代区域2 ──▶ 标记 │
│ 线程3 ──▶ 老年代区域3 ──▶ 标记 │
│ 线程4 ──▶ 老年代区域4 ──▶ 标记 │
│ │
│ N 个线程并行标记、压缩 │
│ │
└──────────────────────────────────────────────┘Parallel GC 的使用
bash
# 启用 Throughput GC(Parallel Scavenge + Parallel Old)
java -XX:+UseParallelGC MyApp
# 等价于(显式指定):
java -XX:+UseParallelGC -XX:+UseParallelOldGC MyApp核心参数
吞吐量控制
bash
# 设置目标吞吐量(GC 时间 / 总时间)
# -XX:GCTimeRatio=N:GC 时间占总时间不超过 1/(N+1)
# 默认 GCTimeRatio=19 → GC 时间 ≤ 5%
java -XX:GCTimeRatio=19 MyApp # 目标吞吐量 95%
# 例子:
# GCTimeRatio=9 → 目标吞吐量 90%(GC ≤ 10%)
# GCTimeRatio=99 → 目标吞吐量 99%(GC ≤ 1%)停顿时间控制
bash
# 设置最大停顿时间目标(软目标,不是硬限制)
java -XX:MaxGCPauseMillis=200 MyApp
# 目标:GC 停顿不超过 200ms
# JVM 会自动调整堆大小和 GC 策略来满足这个目标自适应调节
bash
# 启用自适应调节(默认开启)
java -XX:+UseAdaptiveSizePolicy MyApp
# 自适应调节会自动调整:
# - Survivor 比例
# - 晋升阈值
# - 年轻代大小
# 目的是在满足吞吐量和停顿时间目标的前提下,
# 尽量优化内存使用并行线程数
bash
# 设置 GC 并行线程数
java -XX:ParallelGCThreads=8 MyApp
# 默认值 = CPU 核心数
# 建议:如果机器有超线程,设置为核心数的一半或相等Parallel GC 的 GC 日志
bash
java -XX:+UseParallelGC \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-XX:+PrintGCTimeStamps \
MyApp日志示例:
# 年轻代收集(Parallel Scavenge)
2026-03-22T10:00:00.123: [GC
[PSYoungGen: 1024000K->204800K(1187840K)]
1024000K->204800K(2097152K), 0.0456789 secs]
[Times: user=0.18 sys=0.01, real=0.05 secs]
# ↑
# user 时间(多线程累加)> real 时间
# 老年代收集(Parallel Old)
2026-03-22T10:00:30.456: [Full GC
[PSYoungGen: 204800K->0K(1187840K)]
[ParOldGen: 1572864K->1048576K(1572864K)]
1777664K->1048576K(2760704K), 1.2345678 secs]
[Times: user=4.92 sys=0.08, real=1.23 secs]Parallel GC 的特点
吞吐量优先的设计
吞吐量 = 应用运行时间 / (应用运行时间 + GC 时间)
Parallel GC 的优化方向:
→ 最大化吞吐量
→ 但允许较长的单次停顿
Throughput vs 停顿时间权衡:
┌─────────────────────────────────────────────┐
│ │
│ Parallel GC ──▶ 高吞吐量 ──▶ 长停顿 │
│ │
│ G1 ──────────▶ 平衡 ──────────▶ 中停顿 │
│ │
│ ZGC ─────────▶ 低延迟 ─────────▶ 短停顿 │
│ │
└─────────────────────────────────────────────┘自适应调节详解
当开启 -XX:+UseAdaptiveSizePolicy 时,JVM 会自动调整:
| 参数 | 自动调节 | 说明 |
|---|---|---|
| SurvivorRatio | 自动调整 | Eden:Survivor 比例 |
| MaxTenuringThreshold | 自动调整 | 晋升老年代的年龄阈值 |
| 年轻代大小 | 自动调整 | 根据停顿时间目标调整 |
bash
# 示例:设置吞吐量目标,让 JVM 自动调优
java -XX:+UseParallelGC \
-XX:GCTimeRatio=19 \ # 目标吞吐量 95%
-XX:MaxGCPauseMillis=500 \ # 目标最大停顿 500ms
-XX:+UseAdaptiveSizePolicy \
MyAppParallel GC vs 其他收集器
| 维度 | Parallel GC | G1 | ZGC |
|---|---|---|---|
| 吞吐量 | ⭐⭐⭐⭐⭐ 最高 | ⭐⭐⭐ 中 | ⭐⭐ 低 |
| 停顿时间 | ⭐ 长 | ⭐⭐ 可预测 | ⭐⭐⭐⭐ 亚毫秒 |
| 堆大小影响 | 堆越大停顿越长 | 可控 | 几乎不受影响 |
| 适合场景 | 批处理、离线计算 | Web 服务 | 低延迟场景 |
| JDK 版本 | 1.4+ | 7+(JDK 9 默认) | 11+ |
本节小结
Parallel GC 的核心要点:
| 维度 | Parallel Scavenge | Parallel Old |
|---|---|---|
| 管理区域 | 年轻代 | 老年代 |
| 算法 | 复制 | 标记-压缩 |
| 线程数 | 多线程并行 | 多线程并行 |
| 设计目标 | 高吞吐量 | 高吞吐量 |
| 自适应调节 | 支持 | 支持 |
Parallel GC 适合那些吞吐量优先、对停顿时间不敏感的场景——批处理作业、数据分析、科学计算等。
下一节,我们来看 CMS 回收器(原理/特点/参数/弊端)。
