新服务先使用目标 JDK 的默认收集器并测量。只有基线不能满足暂停或吞吐预算时,才比较其他收集器。CMS 已从现代 JDK 移除,不应作为 Java 21/25 新项目选项。
Table of contents
Open Table of contents
先把目标写成数字
“低延迟”不是目标。收集器选型至少需要这些输入:
- 堆上限与稳定存活集大小。
- 请求吞吐和 CPU 预算。
- P99/P99.9 延迟及单次暂停预算。
- 容器内存上限与进程非堆开销。
- 是否允许用额外 CPU 和内存换更短暂停。
同一收集器在 512 MiB 堆和 64 GiB 堆上的结果不能互相外推。
比较现代 HotSpot 收集器
| 收集器 | 主要目标 | 适合起点 | 关键代价 |
|---|---|---|---|
| Serial | 小占用、简单 | 小数据集、单核工具 | 所有 GC 工作单线程暂停 |
| Parallel | 最大化吞吐 | 批处理、可接受长暂停 | 尾延迟较高 |
| G1 | 平衡吞吐与可预测暂停 | 大多数服务端应用 | 仍可能出现较长暂停或 Full GC |
| ZGC | 极低暂停 | 严格尾延迟、大堆服务 | 更多并发 CPU 与内存开销 |
JDK 25 的可用收集器说明指出,G1 在多数 服务器配置中是默认选项;ZGC 面向高优先级响应时间,暂停不随堆大小线性增长。
CMS 为什么只用于理解旧系统
CMS(Concurrent Mark Sweep)通过并发标记和清除降低老年代暂停,但会产生碎片,并在并发失败时退化。JDK 9 将它标记 为废弃,JDK 14 移除实现。迁移旧系统时需要读懂 CMS 日志与参数,新项目不能在 Java 21/25 启用它。
旧 CMS 服务迁移到 G1 或 ZGC 时不要逐项翻译参数。不同收集器的代际结构、触发策略和日志语义不同,应回到业务目标 重新建立基线。
用相同负载做 A/B 实验
分别运行同一产物、同一数据和同一负载:
java -XX:+UseG1GC -Xms2g -Xmx2g -Xlog:gc*:file=g1.log ...
java -XX:+UseZGC -Xms2g -Xmx2g -Xlog:gc*:file=zgc.log ...
比较应用吞吐、P99/P99.9、GC 暂停分布、分配速率、进程 RSS 和 CPU。不要只比较 GC 日志中的最长暂停;并发 GC 使用的 CPU 也会影响业务线程。
选型结论模板
在 2 GiB 堆、4 vCPU、目标 2,000 RPS 的负载下:
- 延迟预算:P99 < 120 ms,单次 JVM 暂停 < 50 ms
- G1:P99=...,暂停 P99=...,CPU=...
- ZGC:P99=...,暂停 P99=...,CPU=...
- 决策:选择 ...,因为 ...;当堆或流量达到 ... 时复测
这份结论保留测试条件,后续 JDK 升级或流量变化时才能复验。
下一步
阅读JVM 参数、日志与容器资源识别,建立可审计的启动模板。