ZGC与Shenandoah低延迟GC
GC 日志看起来乱,关键是找准几个核心指标。很多开发者面对 GC 日志不知道该关注什么。本文从实际调优经验出发,讲需要关注什么、忽略什么,帮你快速定位问题。
设计目标
| 收集器 | 目标停顿时间 | 最大堆内存 | JDK版本 |
|---|---|---|---|
| G1 | ~200ms | 数十GB | 9+ |
| ZGC | <10ms | TB级 | 11+ (15正式) |
| Shenandoah | <10ms | TB级 | 12+ (OpenJDK) |
ZGC 核心原理
#
着色指针(Colored Pointers)
64位指针结构(使用42~45位): |
作用:直接在指针中存储状态信息,避免访问对象头。
#
读屏障(Load Barrier)
// 访问对象时触发 |
#
并发整理
阶段1:并发标记 |
关键:ZGC 在重定位期间,旧对象和新对象同时存在,读屏障确保访问正确的对象。
#
ZGC 参数
-XX:+UseZGC # 启用ZGC |
Shenandoah 核心原理
#
Brooks Pointer
对象头结构: |
作用:通过对象头中的 Brooks Pointer 实现并发复制。
#
回收阶段
1. Init Mark(初始标记) - STW,标记GC Roots |
#
Shenandoah 参数
-XX:+UseShenandoahGC # 启用Shenandoah |
ZGC vs Shenandoah
| 特性 | ZGC | Shenandoah |
|---|---|---|
| 厂商 | Oracle JDK / OpenJDK | OpenJDK(Red Hat) |
| 核心技术 | 着色指针 + 读屏障 | Brooks Pointer + 读屏障 |
| 停顿时间 | <10ms | <10ms |
| 吞吐量 | ~15%下降 | ~15%下降 |
| 内存占用 | 较高(多映射) | 较高(Brooks Pointer) |
| 平台支持 | Linux/x64, Linux/AArch64, Windows/macOS(JDK 14+) | 全平台 |
| 分代收集 | JDK 21+ 支持 | 不支持 |
适用场景
#
1. 超大堆内存
# 100GB 堆内存 |
传统 GC 在大堆下停顿时间会达到秒级,ZGC/Shenandoah 保持毫秒级。
#
2. 低延迟交易系统
# 金融交易,要求99.9%请求<10ms |
#
3. 交互式应用
# 游戏服务器、实时通信 |
性能对比
测试环境:32核, 128GB内存, JDK 17 |
使用建议
| 场景 | 推荐 |
|---|---|
| 堆 < 6GB | G1 |
| 6GB < 堆 < 100GB,延迟敏感 | ZGC / Shenandoah |
| 堆 > 100GB | ZGC |
| 使用 Oracle JDK | ZGC |
| 使用 OpenJDK | 两者均可 |
| JDK 8/11 | G1(ZGC实验性) |
| JDK 17+ | ZGC / Shenandoah(生产可用) |
| JDK 21+ | 分代ZGC(推荐) |
监控
# ZGC 日志 |
总结
ZGC 和 Shenandoah 代表了 JVM 垃圾回收的发展方向:
- 并发化:更多阶段与用户线程并发
- 细粒度:Region 化、着色指针等技术
- 可扩展:支持 TB 级堆内存
随着 JDK 版本迭代,ZGC 和 Shenandoah 越来越成熟。对于延迟敏感型应用,JDK 17+ 环境下可以大胆使用。
核心要点
关注 Minor GC 和 Full GC 的频率和耗时
年轻代晋升到老年代的对象大小和频率
GC 前后的内存使用变化
使用 jstat、jmap、jvisualvm 等工具辅助分析
总结
GC 调优是一个持续的过程,没有一劳永逸的方案。需要结合业务特点、数据量、响应时间要求来调整。理解 GC 日志是调优的第一步。