经典垃圾回收器
在 G1 成为默认回收器之前,经典回收器组合是 JVM 调优的主战场。理解它们的代际分工、并发策略与停顿特征,是阅读老项目 GC 日志的前提。
回收器代际组合
回收器按"负责的区域"分为新生代与老年代两组,可自由组合:
新生代 老年代
Serial ────────────────→ Serial Old
ParNew ────────────────→ CMS
Parallel Scavenge ─────→ Parallel OldJDK 8 默认组合:Parallel Scavenge + Parallel Old。
新生代回收器
Serial
单线程、串行回收的收集器。GC 时暂停所有工作线程(STW),单线程执行。
新生代:Serial 老年代:Serial Old- 简单高效,单线程下没有线程切换开销
- 适合客户端应用、单核环境、资源受限场景
- 仍是 HotSpot 在 Client 模式下的默认新生代收集器
ParNew
Serial 的多线程版本,使用与 Serial 相同的复制算法,但并行执行。
| 参数 | 作用 |
|---|---|
-XX:+UseParNewGC | 指定使用 ParNew |
-XX:ParallelGCThreads | 并行线程数(默认 = CPU 数) |
- 是 JDK 8 以前唯一能与 CMS 搭配的新生代收集器
- 多核环境下吞吐与停顿优于 Serial,单核下无优势
Parallel Scavenge
关注点与其他收集器不同:目标是可控的吞吐量(高吞吐优先),而非最短停顿。
| 参数 | 作用 |
|---|---|
-XX:MaxGCPauseMillis | 控制最大停顿时间(软目标) |
-XX:GCTimeRatio | 吞吐量目标(默认 99,即 GC 时间 ≤1%) |
-XX:+UseAdaptiveSizePolicy | 自动调节新生代/Eden/Survivor 比例 |
吞吐量 = 运行用户代码时间 /(运行用户代码时间 + GC 时间)。Parallel Scavenge 可通过 UseAdaptiveSizePolicy 让虚拟机自动调优各分区大小,这是它独有的"自适应调节"能力。
老年代回收器
Serial Old
Serial 的老年代版本,采用标记-整理算法,单线程。
- 主要用于 JDK 5 之前与 Parallel Scavenge 搭配
- 也作为 CMS 并发收集失败(Concurrent Mode Failure)时的后备方案
Parallel Old
Parallel Scavenge 的老年代版本,标记-整理算法,多线程并行。
- 与 Parallel Scavenge 组合成为 JDK 8 默认的高吞吐组合
- 兼顾吞吐与老年代回收效率
CMS(Concurrent Mark Sweep)
以最短回收停顿为目标的老年代收集器,基于标记-清除算法。这是第一款真正意义上的并发收集器,其流程分四步:
初始标记(STW 短):仅标记 GC Roots 直接可达对象
并发标记:与用户线程并发遍历对象图
重新标记(STW 短):修正并发期间变化的引用(增量更新)
并发清除:与用户线程并发清除垃圾CMS 的优缺点
| 优点 | 缺点 |
|---|---|
| 并发收集,停顿极短 | 标记-清除产生碎片 |
| 适合响应优先的 Web 应用 | 占用 CPU 资源,降低吞吐 |
| 无法处理浮动垃圾,可能触发 Concurrent Mode Failure | |
| 老年代空间不足时退化 Full GC |
关键参数
| 参数 | 作用 |
|---|---|
-XX:+UseConcMarkSweepGC | 启用 CMS |
-XX:CMSInitiatingOccupancyFraction | 老年代占用率阈值,触发 CMS(默认 92) |
-XX:+UseCMSCompactAtFullCollection | Full GC 后压缩碎片 |
-XX:CMSFullGCsBeforeCompaction | 隔几次 Full GC 压缩一次 |
-XX:+CMSParallelRemarkEnabled | 重新标记阶段并行 |
Concurrent Mode Failure
并发标记阶段用户线程仍在运行,可能产生浮动垃圾。若老年代空间在 CMS 完成前被占满,会抛 Concurrent Mode Failure,退化为 Serial Old 的 Full GC(STW 全停)。
异常:Concurrent Mode Failure
原因:并发阶段老年代空间耗尽
对策:调低 CMSInitiatingOccupancyFraction,提前触发 CMS组合对比速查
| 组合 | 算法 | 目标 | 适用场景 |
|---|---|---|---|
| Serial + Serial Old | 复制 + 标记-整理 | 简单可靠 | 单核、客户端、低内存 |
| ParNew + CMS | 复制 + 标记-清除 | 低停顿 | 响应敏感的服务端(JDK 8 前主流) |
| Parallel Scavenge + Parallel Old | 复制 + 标记-整理 | 高吞吐 | 后台计算、批量任务(JDK 8 默认) |
常见问题
- 为什么 ParNew 必须与 CMS 搭配? 因为 ParNew 是唯一与 CMS 兼容的新生代收集器,CMS 在并发阶段需要与新生代收集器协同维护记忆集。
- CMS 为什么有碎片问题? 标记-清除算法不移动对象,老年代碎片积累后大对象分配失败,触发 Full GC 并压缩。
- 如何选择? 响应优先选 ParNew+CMS(JDK 8 时代)或直接上 G1;吞吐优先选 Parallel 组合。