# Java 中常见的垃圾收集器有哪些?
7 min read
Table of Contents
Java 中常见的垃圾收集器有哪些?
Java 常见垃圾收集器面试标准答案
【核心定义】
Java 垃圾收集器是JVM内存管理系统的核心组件,负责自动回收不再使用的对象以释放内存,不同的收集器在吞吐量、延迟和内存占用之间有不同的权衡策略。
【关键要点】
- Serial 收集器:单线程串行收集器,新生代采用复制算法,老年代采用标记-整理算法,适用于客户端应用或资源受限环境。
- Parallel Scavenge/Parallel Old:多线程并行收集器,新生代采用复制算法,老年代采用标记-整理算法,以吞吐量优先为目标。
- ParNew 收集器:Serial 收集器的多线程版本,新生代收集器,常与CMS配合使用。
- CMS(Concurrent Mark-Sweep):并发标记清除收集器,老年代收集器,以最短停顿时间为目标,采用标记-清除算法。
- G1(Garbage-First):面向服务端应用的收集器,采用分区算法,可预测停顿时间,JDK 9+成为默认收集器。
- ZGC(Z Garbage Collector):JDK 11引入的低延迟收集器,停顿时间不超过10ms,支持TB级堆内存。
- Shenandoah:Red Hat开发的低延迟收集器,与ZGC竞争,JDK 12引入。
【深度推导/细节】
核心矛盾:吞吐量 vs 延迟
- 吞吐量优先:Parallel Scavenge系列,适合后台计算型应用(如批处理),GC时间占总运行时间的比例最小化
- 低延迟优先:CMS/G1/ZGC/Shenandoah,适合交互式应用(如Web服务),单次GC停顿时间最小化
关键数字与设计合理性
-
CMS的4阶段设计:
- 初始标记(STW):标记GC Roots直接关联对象,停顿短
- 并发标记:与用户线程并发执行,标记所有可达对象
- 重新标记(STW):修正并发标记期间的变化,停顿较短
- 并发清除:与用户线程并发执行,清理垃圾对象
-
G1的分区与预测模型:
- 将堆划分为多个Region(默认2048个)
- 维护每个Region的回收价值(回收空间大小/回收时间)
- 每次回收价值最高的Region(Garbage-First名称来源)
- 可设置期望停顿时间(-XX:MaxGCPauseMillis )
-
ZGC的染色指针技术:
- 在指针中嵌入元数据(4位),标记对象状态
- 实现并发转移,无需STW的压缩阶段
- 支持NUMA-aware内存分配
【关联/对比】
收集器组合关系
新生代收集器 老年代收集器 适用场景Serial Serial Old 客户端/小内存Parallel Scavenge Parallel Old 吞吐量优先ParNew CMS 低延迟(JDK 8及之前)G1(统一管理) 平衡型(JDK 9+默认)算法演进对比
| 收集器 | 算法特点 | 停顿时间 | 吞吐量 | 内存碎片 |
|---|---|---|---|---|
| Serial | 标记-整理/复制 | 长 | 中等 | 无 |
| Parallel | 并行标记-整理 | 中等 | 高 | 无 |
| CMS | 并发标记-清除 | 短 | 中等 | 有 |
| G1 | 分区标记-整理 | 可预测 | 高 | 较少 |
| ZGC | 并发标记-复制转移 | 极短 | 高 | 无 |
版本演进路线
- JDK 7/8:Parallel为默认,CMS为低延迟选择
- JDK 9:G1成为默认收集器
- JDK 11:引入ZGC(实验性)
- JDK 15:ZGC和Shenandoah转为正式功能
- JDK 21:ZGC性能大幅优化,支持分代收集
【线程安全与并发机制】
- 安全点(Safepoint)机制:所有收集器都需要在安全点暂停用户线程
- 并发 vs 并行:
- 并行:多个GC线程同时工作(Parallel)
- 并发:GC线程与用户线程同时工作(CMS/G1/ZGC)
- 写屏障(Write Barrier):
- CMS:增量更新(Incremental Update)
- G1:SATB(Snapshot-At-The-Beginning)
- ZGC:负载屏障(Load Barrier)
『面试官追问』
高频问题1:CMS为什么被废弃?
- 内存碎片问题:标记-清除算法产生碎片,Full GC时压缩停顿长
- 并发模式失败:并发期间老年代空间不足,退化为Serial Old
- 浮动垃圾:并发清理期间新产生的垃圾无法回收
- JDK 9被标记为废弃,JDK 14正式移除
高频问题2:G1如何实现可预测停顿?
Step 1: 初始标记(STW) - 标记GC Roots直接引用Step 2: 根区域扫描 - 扫描Survivor区到老年代引用Step 3: 并发标记 - 标记整个堆的可达对象Step 4: 最终标记(STW) - 处理SATB记录Step 5: 筛选回收 - 根据停顿预测模型选择Region回收关键:维护Region的回收价值优先队列,每次选择性价比最高的Region回收。
高频问题3:ZGC vs G1性能对比
- 停顿时间:ZGC < 10ms,G1通常10-200ms
- 吞吐量损失:ZGC约15%,G1约10%
- 内存占用:ZGC需要额外内存存储转发表
- 适用场景:ZGC适合超大堆和严格延迟要求,G1适合通用场景
高频问题4:如何选择收集器?
- 小堆/客户端:Serial(-XX:+UseSerialGC)
- 吞吐量优先:Parallel(-XX:+UseParallelGC)
- 低延迟(JDK 8):ParNew+CMS(-XX:+UseParNewGC -XX:+UseConcMarkSweepGC)
- 平衡型(JDK 8+):G1(-XX:+UseG1GC)
- 超大堆/极低延迟:ZGC(-XX:+UseZGC)或Shenandoah(-XX:+UseShenandoahGC)
高频问题5:分代ZGC(JDK 21)的优势
- 年轻代对象朝生夕死,单独收集效率更高
- 减少老年代扫描压力
- 降低写屏障开销
- 整体吞吐量提升,停顿时间进一步降低
最佳实践:生产环境应根据应用特点(堆大小、延迟要求、吞吐量需求)进行压测,选择最适合的收集器。JDK 11+推荐从G1开始,如有严格延迟要求再考虑ZGC。