垃圾回收器的暂停问题,对于那些对实时响应有严苛要求的服务来说,一直是个令人头疼的难题。像 CMS 和 G1 这类主流回收器,动辄数十甚至上百毫秒的停顿时间,在高敏感场景下堪称致命。更不用说,针对它们的调优本身门槛就不低,需要对内部机制有相当了解才能奏效。不过,随着 ZGC(Z Garbage Collector)的出现,这个痛点迎来了转机。ZGC 最初在 JDK 11 中作为实验性功能亮相,在 JDK 15 中宣布生产就绪。考虑到 JDK 17 才是第一个真正面向大众的长期支持(LTS)版本,并且已有不少公司开始采用,本文将重点围绕 JDK 17 展开。
ZGC 作为一款设计目标明确的低延迟垃圾收集器,旨在满足以下几个核心指标:
- 支持 8MB 到 16TB 的堆内存大小。
- 保证最大 GC 暂停时间不超过 10 毫秒。
- 在最坏情况下,吞吐量降低不超过 15%(根据实测,如果参数配置不当,可能影响更大。可以理解为这是一种用额外的 CPU 资源来换取极致 GC 暂停时间的权衡策略)。
核心优势:无法拒绝的低延迟
毫秒级 GC 暂停,满足极致低延迟业务需求
根据美团技术团队分享的数据,在 Zeus 服务的不同集群中,启用 ZGC 为低延迟(TP999 < 200ms)场景带来了显著收益:
- TP999:下降了 12~142ms,降幅达 18%~74%。
- TP99:下降了 5~28ms,降幅达 10%~47%。
这种级别的 GC 停顿改善,是使用 CMS、Parallel GC 或 G1 等传统回收器时,无论如何精细调优都难以企及的。它能确保应用在 JVM 层面的卡顿被控制在 1 毫秒以内,这正是升级 JDK 17 并启用 ZGC 最具说服力的理由。
当然,除了低延迟,升级 JDK 17 还有一些额外的“甜点”:
- 生态演进:新版 Spring Boot 官方已将最低支持版本设为 JDK 17,想使用最新的 Spring 特性,升级是必经之路。
- 性能与安全:JIT 编译器得到增强,同时包含 Sealed 类、模式匹配、Records 等新语言特性。升级也能获得更好的安全性,包括已修复的漏洞和强化的安全机制。
适用场景建议
- 推荐场景:网关服务、Web API 等对响应延迟敏感的服务。
- 暂不推荐:CPU 密集型的定时任务、批量处理任务。
升级前后性能对比
让我们直接看数据,这是最直观的。
测试环境:
CPU: 4c
Mem: 6GB
G1 GC 参数配置:
-Xmx3500m -Xms3500m -XX:+UseG1GC -XX:MaxGCPauseMillis=100
-XX:G1ReservePercent=10 -XX:ConcGCThreads=2 -XX:ParallelGCThreads=5
-XX:G1HeapRegionSize=16m -XX:MaxTenuringThreshold=14
-XX:SurvivorRatio=8
ZGC 参数配置:
--add-opens=java.base/java.lang=ALL-UNNAMED -Xms3500m -Xmx3500m -XX:ReservedCodeCacheSize=256m -XX:InitialCodeCacheSize=256m -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -XX:ConcGCThreads=1 -XX:ParallelGCThreads=3 -XX:ZCollectionInterval=60 -XX:ZAllocationSpikeTolerance=4 -XX:+UnlockDiagnosticVMOptions -XX:-ZProactive -Xlog:safepoint,classhisto*=trace,age*,gc*=info:file=/opt/gc-%t.log:time,tid,tags:filecount=5,filesize=50m
上述两套参数均已在生产环境验证,对应的机器单机业务 TPS 约为 1500。
GC 耗时对比

从上图可以清晰地看到,GC 耗时有着质的飞跃。G1 GC 的暂停在数十毫秒级别波动,而 ZGC 的停顿被稳定地压制在 1 毫秒左右。这种差距是传统垃圾回收器难以通过调优达到的,它从根本上降低了JVM垃圾回收对服务响应时间的影响。
CPU 使用率对比

在 CPU 使用率方面,运行相同的业务代码,使用 JDK 17 与 ZGC 的组合,会比 JDK 8 搭配旧回收器高出约 10% ~ 20%。这印证了前面提到的“以CPU换时间”的策略,ZGC 通过更频繁地利用并发线程进行垃圾回收工作,来换取极致的低停顿。这对于追求低延迟的后端服务架构来说,通常是值得的投入。
升级与配置实践
1. JDK 17 安装
首先需要在目标环境安装 JDK 17。以下提供几种常见方式的示例:
# Ubuntu / Debian 系统安装
sudo apt install openjdk-17-jdk
# Docker 基础镜像选择
docker pull openjdk:17-slim
docker pull openjdk:17-jdk-oraclelinux7
# Dockerfile 示例
FROM openjdk:17-slim
2. JVM 参数调整
安装好 JDK 17 后,下一步就是配置 JVM 参数以启用和优化 ZGC。下面是一组经过生产环境验证的参考参数,你可以根据实际情况调整换行格式:
--add-opens=java.base/java.lang=ALL-UNNAMED \
-Xms1500m -Xmx1500m \
-XX:ReservedCodeCacheSize=256m \
-XX:InitialCodeCacheSize=256m \
-XX:+UnlockExperimentalVMOptions \
-XX:+UseZGC \
-XX:ConcGCThreads=1 -XX:ParallelGCThreads=2 \
-XX:ZCollectionInterval=30 -XX:ZAllocationSpikeTolerance=5 \
-XX:+UnlockDiagnosticVMOptions -XX:-ZProactive \
-Xlog:safepoint,classhisto*=trace,age*,gc*=info:file=/opt/gc-%t.log:time,tid,tags:filecount=5,filesize=50m \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/opt/errorDump.hprof
关键参数释义
为了方便理解,下表对上述部分关键 JVM 参数进行了说明:

总而言之,对于响应延迟要求极高的现代Java服务,从 JDK 8 升级至 JDK 17 并启用 ZGC,是一项能带来显著收益的技术决策。它并非简单的版本更新,而是通过新一代垃圾回收器的能力,从根本上优化了应用的响应确定性。当然,任何升级都需要经过充分的测试,建议在预发或小流量环境中验证后再全量推广。如果你想了解更多关于 JVM 调优或微服务架构的实践,可以到 云栈社区 与更多开发者交流探讨。