经典垃圾收集器与组合#
一句话答案#
HotSpot 的收集器分新生代和老年代两类:新生代有 Serial(单线程)、ParNew(多线程,CMS 搭档)、Parallel Scavenge(吞吐量优先);老年代有 Serial Old、Parallel Old(吞吐量优先)、CMS(低停顿);G1 之后是面向整堆的 Region 收集器。选型核心是在「吞吐量」和「停顿时间」之间权衡:Parallel 系列追吞吐,CMS/G1/ZGC 追低停顿。
核心要点
1. 收集器按代划分(连线表示可搭配组合):
新生代 老年代
┌──────────────┐ ┌──────────────┐
│ Serial │────────▶│ Serial Old │ 客户端/小堆,单线程,STW
│ ParNew │────────▶│ CMS │ 低停顿经典组合
│ Parallel │────────▶│ Parallel Old │ 吞吐量优先(JDK8 默认)
│ Scavenge │ │ │
└──────────────┘ └──────────────┘
G1 / ZGC / Shenandoah:不分代连线,面向整堆 Regionplaintext2. 各收集器一句话:
| 收集器 | 区域 | 线程 | 算法 | 目标 | 关键点 |
|---|---|---|---|---|---|
| Serial | 新生代 | 单线程 | 复制 | — | 最简单高效(无线程切换开销),客户端模式默认 |
| ParNew | 新生代 | 多线程 | 复制 | 低停顿 | Serial 的多线程版,唯一能和 CMS 搭配的新生代收集器 |
| Parallel Scavenge | 新生代 | 多线程 | 复制 | 吞吐量 | 可控吞吐量(GC 时间占比),有自适应调节 -XX:+UseAdaptiveSizePolicy |
| Serial Old | 老年代 | 单线程 | 标记-整理 | — | CMS 失败时的后备方案 |
| Parallel Old | 老年代 | 多线程 | 标记-整理 | 吞吐量 | 与 Parallel Scavenge 搭配,JDK8 默认组合 |
| CMS | 老年代 | 多线程并发 | 标记-清除 | 低停顿 | 详见 CMS收集器,有碎片+浮动垃圾 |
| G1 | 整堆 | 多线程并发 | 整体标记-整理/局部复制 | 可预测停顿 | 详见 G1收集器原理,JDK9 默认 |
| ZGC | 整堆 | 几乎全并发 | 染色指针+读屏障 | <10ms 停顿 | 详见 ZGC特性,超大堆 |
3. 默认收集器演进:
- JDK 8:Parallel Scavenge + Parallel Old(吞吐量优先)
- JDK 9 ~ 至今:G1
- JDK 11+:ZGC/Shenandoah 实验→转正(按版本)
4. 吞吐量 vs 停顿时间(核心权衡):
- 吞吐量 = 运行用户代码时间 /(用户代码时间 + GC 时间)。批处理、科学计算等后台任务关注吞吐 → Parallel 系列。
- 停顿时间(STW 时长):在线服务、对延迟敏感 → CMS/G1/ZGC。
- 两者难以兼得:要低停顿就得并发收集,并发会占 CPU、降低吞吐。
面试回答(2分钟版)
HotSpot 的经典收集器按分代分两类。新生代有三个:Serial 是单线程的,用复制算法,简单高效,适合客户端和小堆;ParNew 是 Serial 的多线程版本,它最大的特点是唯一能和 CMS 搭配的新生代收集器;Parallel Scavenge 也是多线程复制,但它的目标是吞吐量,可以设置吞吐量大小还能自适应调节。老年代有三个:Serial Old 单线程标记-整理,常作为 CMS 失败的后备;Parallel Old 是 Parallel Scavenge 的老年代搭档,多线程标记-整理,这俩组合是 JDK8 的默认收集器,主打吞吐量;CMS 用标记-清除追求低停顿,但有内存碎片和浮动垃圾的问题。再往后就是不分代、面向整堆 Region 的 G1,是 JDK9 之后的默认,再往后是停顿可以做到 10ms 以内的 ZGC。选型的核心就是吞吐量和停顿时间的权衡:批处理这种后台任务选 Parallel 系列追吞吐,在线服务对延迟敏感就选 CMS、G1 或 ZGC 追低停顿,但低停顿要并发收集,会多占 CPU、牺牲一点吞吐。
追问与易错
追问方向:
- “ParNew 和 Parallel Scavenge 都是多线程新生代,区别是什么?”→ ParNew 目标是配合 CMS 做低停顿,能和 CMS 搭配;Parallel Scavenge 目标是吞吐量,有自适应调节,但不能和 CMS 搭配。关注点不同:一个低停顿、一个高吞吐
- “为什么只有 ParNew 能配 CMS,Parallel Scavenge 不行?”→ 历史实现原因,CMS 和 Parallel Scavenge 的框架代码不兼容(Parallel Scavenge 没有用 HotSpot 那套分代框架),所以 CMS 只能搭 Serial 或 ParNew
- “Serial 单线程为什么还存在?多线程不是更快吗?”→ 单线程没有线程交互/切换开销,在客户端模式、小内存(几十到一两百 MB)场景下停顿可控且简单高效;嵌入式、桌面程序仍适用
- “什么是吞吐量优先?怎么配置?”→ Parallel Scavenge 用
-XX:MaxGCPauseMillis控制最大停顿、-XX:GCTimeRatio控制吞吐量比例,-XX:+UseAdaptiveSizePolicy让 JVM 自动调新生代大小和晋升阈值 - “现在生产上还用 CMS / Parallel 吗?”→ CMS 在 JDK9 被标记废弃、JDK14 移除;新项目主流是 G1,超大堆低延迟用 ZGC;Parallel 仍适合纯吞吐的离线批处理
易错点:
- ❌ “Parallel 和 CMS 都是并行收集器所以能搭配”——Parallel Scavenge 不能配 CMS,只有 ParNew 能
- ❌ 混淆「并行 Parallel」(多条 GC 线程同时干活,用户线程仍 STW)和「并发 Concurrent」(GC 线程和用户线程同时运行)
- ❌ “单线程一定比多线程慢”——小堆下 Serial 因无切换开销反而更优