ZGC与Shenandoah低延迟GC

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位):
├─ 41位:对象地址(4TB堆)
├─ 4位:元数据
│ ├── Finalizable
│ ├── Remapped
│ ├── Marked1
│ └── Marked0
└─ 17位:保留

指针值:0x0001_2345_6789_ABCD
││││
│││└─ Marked0
││└── Marked1
│└─── Remapped
└──── Finalizable

作用:直接在指针中存储状态信息,避免访问对象头。

#

读屏障(Load Barrier)

// 访问对象时触发
Object obj = field.x; // 读操作

// 读屏障检查指针状态
if (指针需要重映射) {
修正指针;
}

#

并发整理

阶段1:并发标记
- 标记存活对象(通过着色指针)
- 与用户线程并发

阶段2:并发重定位准备
- 选择需要整理的Region
- 计算新地址

阶段3:并发重定位
- 复制对象到新地址
- 更新指针(Remapped标志)
- 通过读屏障修正旧引用

关键:ZGC 在重定位期间,旧对象和新对象同时存在,读屏障确保访问正确的对象。

#

ZGC 参数

-XX:+UseZGC                    # 启用ZGC
-XX:+ZGenerational # 启用分代ZGC(JDK 21+)
-XX:MaxGCPauseMillis=10 # 目标停顿时间
-XX:ZCollectionInterval=5 # 强制GC间隔(秒)
-XX:ZAllocationSpikeTolerance=2 # 分配速率容忍度

Shenandoah 核心原理

#

Brooks Pointer

对象头结构:
┌─────────────────┐
│ Brooks Pointer │ 指向对象自身或新副本
├─────────────────┤
│ Mark Word │
├─────────────────┤
│ Class Pointer │
├─────────────────┤
│ 实例数据 │
└─────────────────┘

作用:通过对象头中的 Brooks Pointer 实现并发复制。

#

回收阶段

1. Init Mark(初始标记)    - STW,标记GC Roots
2. Concurrent Mark(并发标记)- 遍历标记存活对象
3. Final Mark(最终标记) - STW,处理SATB队列
4. Concurrent Cleanup(并发清理)- 回收垃圾Region
5. Concurrent Evacuation(并发复制)- 复制存活对象
6. Init Update Refs(初始更新引用)- STW,更新GC Roots
7. Concurrent Update Refs(并发更新引用)- 更新堆内引用
8. Final Update Refs(最终更新引用)- STW,完成引用更新

#

Shenandoah 参数

-XX:+UseShenandoahGC           # 启用Shenandoah
-XX:ShenandoahGCHeuristics=adaptive # 自适应策略
-XX:+ShenandoahPacing # 分配 pacing(防止分配过快)

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 堆内存
-Xms100g -Xmx100g -XX:+UseZGC

传统 GC 在大堆下停顿时间会达到秒级,ZGC/Shenandoah 保持毫秒级。

#

2. 低延迟交易系统

# 金融交易,要求99.9%请求<10ms
-XX:+UseZGC -XX:MaxGCPauseMillis=5

#

3. 交互式应用

# 游戏服务器、实时通信
-XX:+UseShenandoahGC

性能对比

测试环境:32核, 128GB内存, JDK 17

G1: 平均停顿 150ms, 最大停顿 500ms, 吞吐量 95%
ZGC: 平均停顿 1ms, 最大停顿 5ms, 吞吐量 85%
Shenandoah: 平均停顿 2ms, 最大停顿 8ms, 吞吐量 87%

使用建议

场景 推荐
堆 < 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 日志
-Xlog:gc*:file=zgc.log:time,level,tags

# 关键指标
# ZGC Cycles: GC周期数
# ZGC Pauses: 停顿时间
# ZGC Heap: 堆使用情况

总结

ZGC 和 Shenandoah 代表了 JVM 垃圾回收的发展方向:

  • 并发化:更多阶段与用户线程并发
  • 细粒度:Region 化、着色指针等技术
  • 可扩展:支持 TB 级堆内存

随着 JDK 版本迭代,ZGC 和 Shenandoah 越来越成熟。对于延迟敏感型应用,JDK 17+ 环境下可以大胆使用。

核心要点

  1. 关注 Minor GC 和 Full GC 的频率和耗时

  2. 年轻代晋升到老年代的对象大小和频率

  3. GC 前后的内存使用变化

  4. 使用 jstat、jmap、jvisualvm 等工具辅助分析

总结

GC 调优是一个持续的过程,没有一劳永逸的方案。需要结合业务特点、数据量、响应时间要求来调整。理解 GC 日志是调优的第一步。


   转载规则


《ZGC与Shenandoah低延迟GC》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录