JVM 中的 GC 算法演进之路!(Serial、CMS、G1 到 ZGC)
引言
想象一下,Java 程序运行就像在一个巨大的图书馆里借书还书。这个图书馆(JVM 的内存堆区)为了高效运转,需要一个聪明的“图书管理员”来清理失效的书籍(垃圾对象)。这,就是垃圾回收器(GC)的使命!
一、什么是 GC?
GC(Garbage Collection)是 JVM 自动内存管理的重要组成部分,负责回收不再被引用的对象所占用的内存空间,防止内存泄露和内存溢出。
1.1 为什么需要 GC?
- 自动内存管理,减轻开发者负担
- 避免野指针、内存泄露等问题
- 保证 Java 应用稳定高效运行
1.2 GC 面临的挑战
- 如何在不打断业务的前提下进行垃圾回收?
- 如何避免过多的 STW(Stop-The-World)?
- 如何在高并发、大内存场景下依然稳定?
二、JVM 内存结构简析(理解 GC 的基础)
JVM 堆区被划分为以下几个区域:
- 新生代(Young Generation):包括 Eden 和两个 Survivor 区
- 老年代(Old Generation):存放长生命周期的对象
- 元空间(Metaspace):替代原有的永久代,存放类的元数据
垃圾回收的重点主要在 新生代和老年代。
三、GC 算法简述
Java GC 的核心算法主要有:
- 复制算法(Copying):用于新生代
- 标记-清除(Mark-Sweep):用于老年代
- 标记-压缩(Mark-Compact):避免内存碎片
- 分代收集理论:新生代频繁回收,老年代少回收
四、GC 发展史:从 Serial 到 ZGC
4.1 Serial GC(串行垃圾回收器)
- 回收机制:新生代使用复制算法,老年代使用标记-压缩算法
- 回收线程:单线程
- 触发机制:内存耗尽或触发 Full GC 时
缺点
- 每次 GC 都会 Stop-The-World,且只能使用单线程
示例
-XX:+UseSerialGC
📌 适用场景:嵌入式、小型应用、单核处理器
4.2 CMS GC(Concurrent Mark Sweep)
CMS 是第一个低延迟为目标的 GC,着眼于缩短老年代的 GC 停顿时间。
工作流程
初始标记(STW) → 并发标记 → 重新标记(STW) → 并发清除
特点
- 多线程并发标记和清除,减少 STW
- 使用 标记-清除 算法,导致内存碎片问题
示例参数
-XX:+UseConcMarkSweepGC
缺点
- 并发失败风险:老年代空间不足时需退化为 Serial Old GC
- 空间碎片影响分配性能
📌 适用场景:中大型系统,对响应时间敏感的 Web 应用
4.3 G1 GC(Garbage First)
G1 GC 是 JDK 9 之后的默认 GC,旨在取代 CMS。
原理
- 将整个堆划分为多个大小一致的 Region(既可作为 Eden、Survivor、Old)
- 基于 Region 的优先级回收策略:优先回收垃圾最多的 Region
- 并发标记后,通过 Evacuation 将存活对象复制到新的 Region,实现压缩
回收流程
初始标记(STW) → 并发标记 → 最终标记(STW) → 筛选回收(STW)
特点
- 支持大堆(数十 GB)
- 可配置 Pause Time(停顿目标)
- 减少 Full GC 的频率
示例参数
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
📌 适用场景:中大型服务端应用,追求吞吐和响应时间平衡
4.4 ZGC(Z Garbage Collector)
ZGC 是一个为低延迟场景设计的 GC,目标是将所有 GC 停顿控制在 10ms 以内。
特点
- 几乎全程并发执行,所有 GC 阶段都不长时间阻塞应用线程
- 使用 染色指针(Colored Pointers) 来标识对象状态
- 通过读屏障和写屏障实现引用更新的同步
技术亮点
- 支持极大的堆内存(最高可达 TB 级)
- 多阶段并发整理,移动对象时应用线程无需停止
示例参数
-XX:+UseZGC
-XX:+UnlockExperimentalVMOptions
📌 适用场景:金融、电商、游戏等低延迟业务
五、不同 GC 的对比总结
特性 | Serial | CMS | G1 | ZGC |
---|---|---|---|---|
停顿时间 | 高 | 中 | 低 | 极低 |
并发回收 | 否 | 是 | 是 | 是 |
内存碎片 | 无 | 有 | 无 | 无 |
吞吐量 | 高 | 中 | 高 | 中 |
响应时间 | 差 | 好 | 更好 | 极佳 |
大内存支持 | 差 | 一般 | 好 | 极好 |
是否压缩整理 | 是 | 否 | 是 | 是 |
六、GC 日志分析建议
可以通过以下 JVM 参数输出 GC 日志:
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log
常见指标:
GC time
:单次回收的耗时freed memory
:回收掉的内存大小pause time
:STW 的具体时间
七、真实案例分析
场景:大数据系统使用 CMS 导致频繁 Full GC
问题: CMS 回收速度跟不上对象创建速度,频繁触发 Full GC,导致延迟剧增。
解决方案: 切换为 G1 GC,并设置合理的 MaxGCPauseMillis
,显著降低了延迟峰值。
八、选择建议
应用场景 | 推荐 GC |
---|---|
单线程、小型程序 | Serial |
中等延迟要求 | CMS |
大型服务端系统 | G1 |
超大堆、极低延迟 | ZGC / Shenandoah |
九、总结与未来展望
JVM 垃圾回收技术不断进化,从最初的串行单线程到如今几乎无感知的并发收集器,反映了 Java 在现代应用场景下对性能、可伸缩性和稳定性的持续追求。
未来,ZGC 与 Shenandoah 的持续优化将成为主流趋势,也许某一天,GC 将真正做到“零成本”!
🔚 尾声
👍 点赞 + ⭐ 收藏,助你 GC 不迷路!
📬 评论聊聊你在使用 GC 中踩过的坑,或者你的调优秘籍~
📌 关注我,带你一起玩转 Java 性能调优!