资讯动态

JDK 8到JDK 17垃圾回收器全解析:G1、ZGC、Shenandoah实战选型指南

发布时间:2026/8/21 4:08:26 来源:尧图企业网站定制
最近在帮一个老项目做技术栈升级从 JDK 8 迁移到 JDK 17。过程中最绕不开、也最让人心里没底的就是垃圾回收器Garbage Collector, GC这块。很多团队升级时可能只是简单地把-XX:UseG1GC从 JDK 8 挪到 JDK 17或者干脆不指定用默认的。但这样做很可能错过了 JDK 17 在 GC 领域带来的真正红利甚至可能因为配置不当把一次性能升级变成了线上事故。问题的核心在于JDK 8 时代我们熟悉的 GC 世界在 JDK 17 已经发生了根本性的变化。这不仅仅是 G1 变得更成熟了而是整个 GC 的“游戏规则”和“选手阵容”都变了。如果你还用五年前的认知去配置今天的 JVM就像拿着旧地图在新城区找路大概率会走错。今天我们不只对比参数而是彻底拆解从 JDK 8 到 JDK 17五大主流垃圾回收器Serial, Parallel, CMS, G1, ZGC究竟经历了什么以及在不同场景下你应该如何做出最务实的选择。1. 先理清现状JDK 8 与 JDK 17 的 GC 格局已截然不同在 JDK 8 的世界里GC 的选择像一场“四选一”的考试。你的主要选项是Serial GC单线程适合客户端或极小内存应用。Parallel GC (Throughput Collector)多线程并行追求高吞吐量JDK 8 的默认选择。CMS (Concurrent Mark-Sweep)并发低延迟收集器旨在减少 STW (Stop-The-World) 停顿但配置复杂且已在 JDK 14 被标记为废弃DeprecatedJDK 17 中已移除。G1 (Garbage-First)面向服务端、追求可预测停顿时间的收集器是未来当时的方向。到了 JDK 17格局演变成了“新旧交替与王者降临”Serial Parallel GC依然健在定位未变作为特定场景的备选。CMS已正式移除。如果你有老脚本指定了-XX:UseConcMarkSweepGC在 JDK 17 上会直接报错。这是升级时必须清理的“历史债务”。G1已成为默认垃圾回收器。这意味着如果你不指定任何 GC 参数JVM 将使用 G1。它经过多年优化在吞吐量和延迟的平衡上更加成熟。ZGC与Shenandoah这两位是“新时代的王者”。它们的设计目标是将 STW 停顿时间控制在10 毫秒以内甚至亚毫秒级且停顿时间几乎不随堆大小增长而增加。ZGC 在 JDK 15 成为生产可用特性Shenandoah 则由 Red Hat 主导贡献。这个变化背后是 Java 应用架构的演进微服务、云原生、大内存数十GB甚至数百GB成为常态。传统的 GC 在应对超大堆时动辄数百毫秒甚至秒级的 GC 停顿已成为不可接受的性能瓶颈。ZGC/Shenandoah 的出现正是为了解决这个核心痛点。所以升级 JDK 17 后选择 GC首先要问自己我的应用最不能忍受的是什么是吞吐量下降还是不可预测的长时间停顿2. 五大回收器深度拆解从原理到适用场景了解格局后我们深入每个回收器看它们如何工作以及适合谁。2.1 Serial GC简单纯粹的“扫地僧”工作原理单线程执行所有垃圾回收工作标记-清除-压缩。进行 GC 时必须暂停所有应用线程STW。JDK 8 vs JDK 17核心逻辑未变。启动参数依然是-XX:UseSerialGC。适用场景客户端 GUI 应用如 Swing/JavaFX对短暂停顿不敏感。资源极度受限的嵌入式环境或微服务内存100MB。用于学习、测试或快速验证想法的场景。为什么不推荐用于主流服务端STW 时间与堆内存大小、存活对象数量直接相关。一旦堆稍大停顿时间就会变得很长严重影响服务响应。2.2 Parallel GC (Throughput Collector)吞吐量至上的“力量型选手”工作原理Serial GC 的多线程并行版本。年轻代和老年代回收都使用多线程并行处理以最大化应用程序的吞吐量即 GC 时间占比最小。但 STW 期间所有应用线程依然会暂停。JDK 8 vs JDK 17JDK 8 的默认 GC。在 JDK 17 中你需要显式指定-XX:UseParallelGC来启用。其变体-XX:UseParallelOldGC并行处理老年代在 JDK 17 中已无区别指定前者即可。核心调优参数-XX:ParallelGCThreads设置并行 GC 线程数通常可设置为 CPU 核心数。-XX:MaxGCPauseMillis期望的最大 GC 停顿时间毫秒。注意这是一个“目标”GC 会牺牲吞吐量和空间来尝试达到它设得太小反而可能导致更频繁的 GC 和总体性能下降。-XX:GCTimeRatio吞吐量目标公式为1 / (1 GCTimeRatio)默认 99即允许 1% 的时间用于 GC。适用场景后台计算密集型应用如批量数据处理、科学计算。对延迟不敏感但追求最大任务处理能力的应用。资源相对充足可以接受为达到吞吐量目标而占用更多内存。坑点提醒不要盲目将-XX:MaxGCPauseMillis设得太小。在堆较大或对象分配率高的情况下过于激进的停顿目标会导致 GC 频繁发生整体吞吐量不升反降。先追求吞吐量稳定再酌情优化停顿。2.3 G1 (Garbage-First)平衡之道的“多面手”工作原理将堆划分为多个大小相等的 Region。采用“标记-整理”算法但主要以“增量式”和“可预测”的方式进行。其核心是“回收价值最大”的 RegionGarbage-First 名字由来即那些垃圾最多的 Region从而在有限时间内获得尽可能高的回收效率。JDK 8 vs JDK 17最大变化是从“备选”变成了“默认”。JDK 17 中的 G1 经过多年优化在并行处理、混合回收、字符串去重等方面更加高效和稳定。对于大多数从 JDK 8 升级的应用G1 是最安全、最值得优先尝试的默认选择。核心调优参数-XX:MaxGCPauseMillis这是 G1 的黄金参数。G1 会极力满足你设定的停顿时间目标比如 200ms并以此为核心来调整年轻代大小、回收节奏等。相比 Parallel GCG1 对这个参数的反应更积极、更有效。-XX:G1HeapRegionSizeRegion 大小可设为 1MB 到 32MB 的 2 的幂。通常无需手动设置G1 会根据堆大小自动计算。-XX:InitiatingHeapOccupancyPercent(IHOP)触发并发标记周期的堆占用阈值默认 45%。如果老年代增长过快可以适当调低此值让 G1 更早开始标记。适用场景JDK 17 升级的默认起点适用于绝大多数需要平衡吞吐量和延迟的服务端应用。堆内存从 4GB 到数十 GB 的范围。要求停顿时间相对可控例如 200-500ms 以内。升级实操建议直接使用默认 G1即不指定任何-XX:Use*GC参数。根据监控如 GC 日志观察停顿时间。如果 99% 的停顿都能满足要求则无需调优。如果发现停顿时间过长首先调整-XX:MaxGCPauseMillis例如从 200 调到 150这通常是最高效的手段。如果调整后效果不佳或引发频繁 GC再考虑分析 GC 日志看是否是 IHOP 或 Region 大小等问题。2.4 ZGC低延迟领域的“革命者”工作原理其核心是“并发”和“基于 Region”的“标记-整理”算法。它通过染色指针和读屏障两大关键技术实现了几乎全并发的垃圾回收。也就是说除了初始标记和重新标记等极短阶段ZGC 的绝大部分工作标记、转移、重定位都与应用线程并发执行STW 时间被缩短到以微秒计且与堆大小无关。JDK 8 vs JDK 17JDK 8 中不存在。在 JDK 17 中它已是生产可用的成熟特性。启用参数为-XX:UseZGC。核心特性与参数亚毫秒级最大停顿时间这是其最大卖点通常能控制在 1ms 以内。堆大小无关的停顿时间无论是 8GB 还是 256GB 的堆最大停顿时间都在同一量级。支持 NUMA自动感知 NUMA 架构提升内存访问性能。压缩指针默认开启节省内存。对于超大堆4TB可能需要使用-XX:-UseCompressedOops关闭。并发线程数-XX:ConcGCThreads可设置并发 GC 线程数通常不需要调整。适用场景对延迟极度敏感的应用如金融交易系统、实时游戏服务器、高频数据采集。使用超大堆内存数十GB以上的应用无法忍受传统 GC 的长停顿。希望获得更稳定、可预测的响应时间的服务。代价与坑点吞吐量开销为了实现超低延迟ZGC 的并发操作会与应用程序竞争 CPU 和内存带宽可能导致吞吐量比 G1 或 Parallel 低 10%-20%。这是用吞吐量换延迟的典型取舍。内存开销需要额外的内存来存储元数据通常比 G1 多占用 10%-20% 的堆内存。JDK 版本确保使用最新的 JDK 17 更新版本以获得最佳性能和稳定性。2.5 Shenandoah与 ZGC 并驾齐驱的“低延迟先锋”工作原理与 ZGC 目标类似但实现技术不同。Shenandoah 也追求低停顿和堆大小无关性它使用“Brooks 指针”和读/写屏障来实现并发整理。它的一个显著特点是其并发压缩算法允许在对象被应用线程访问的同时进行移动。JDK 8 vs JDK 17同样在 JDK 8 中不存在。在 JDK 17 中也是生产可用特性。启用参数为-XX:UseShenandoahGC。需要注意的是Shenandoah 并非所有 JDK 发行版都默认包含如 Oracle OpenJDK 包含但某些厂商的构建可能未包含。与 ZGC 的对比特性ZGCShenandoah最大停顿目标1ms10ms (通常也能做到亚毫秒)关键技术染色指针、读屏障Brooks 指针、读/写屏障吞吐量开销中等中等两者相近具体取决于负载内存开销中等~15%较低~5%JDK 集成Oracle/OpenJDK 主线最初由 Red Hat 贡献现已在主线适用堆大小超大堆优势明显各种堆大小表现均衡如何选择 ZGC 还是 Shenandoah对于绝大多数应用两者都能提供远超传统 GC 的低延迟体验。选择时可以基于发行版确认你的 JDK 发行版包含了你想用的 GC。微基准测试用你的实际应用和负载进行压测观察吞吐量和延迟曲线。差异可能很细微。社区与支持查看你所用的云平台或容器镜像对哪种 GC 有更好的支持或优化。3. 从 JDK 8 升级到 JDK 17GC 迁移实战路线图理论清楚了我们来看实战。从 JDK 8 (假设用 Parallel 或 CMS) 升级到 JDK 17GC 迁移不是简单改个参数而是一个有步骤的验证过程。3.1 第一步清理废弃参数与评估现状首先移除所有 JDK 17 已废弃或移除的 GC 相关参数。最重要的是删除-XX:UseConcMarkSweepGC(CMS)。检查并移除-XX:UseParNewGC(通常与 CMS 配对)。检查-XX:UseParallelOldGC在 JDK 17 中它与-XX:UseParallelGC等效保留一个即可。然后在 JDK 8 环境下通过 GC 日志分析现有应用的 GC 行为基线# JDK 8 启动参数示例用于收集基线数据 java -Xms4g -Xmx4g -XX:UseG1GC -XX:PrintGCDetails -XX:PrintGCDateStamps -Xloggc:gc_baseline.log -jar your-app.jar分析gc_baseline.log关注Full GC 频率和耗时这是延迟的主要杀手。Young GC 频率和平均/最大停顿时间。吞吐量应用运行时间占总时间的比例。3.2 第二步在 JDK 17 上以 G1 为起点进行测试这是最稳妥的路径。在测试环境使用 JDK 17 并让 G1 作为默认 GC或不指定或用-XX:UseG1GC启动应用。# JDK 17 启动参数示例启用详细 GC 日志推荐使用统一日志框架 java -Xms4g -Xmx4g -Xlog:gc*,gcheapdebug,gcergo*trace:filegc_g1_jdk17.log:time,uptime,level,tags -jar your-app.jar关键动作使用相同的负载进行压测比较 JDK 17 G1 与 JDK 8 原有 GC 的日志。目标确保停顿时间P99, P999和吞吐量没有显著退化。通常得益于 JVM 本身的优化性能会有提升。如果 G1 表现满意那么迁移成功。你可以进一步微调-XX:MaxGCPauseMillis。3.3 第三步评估是否需转向 ZGC/Shenandoah如果应用对延迟有极致要求或者基线中出现了无法忍受的长停顿200ms则进入此步骤。启用 ZGCjava -Xms4g -Xmx4g -XX:UseZGC -Xlog:gc:filegc_zgc.log ...启用 Shenandoahjava -Xms4g -Xmx4g -XX:UseShenandoahGC -Xlog:gc:filegc_shenandoah.log ...压测对比要点延迟重点关注P99.9甚至P99.99的响应时间。低延迟 GC 的价值在于消除长尾延迟。吞吐量观察在相同负载下请求处理速率RPS/QPS是否有可接受的下降例如 15%。资源消耗监控 CPU 使用率可能因并发 GC 而升高和内存占用。注意切换到 ZGC/Shenandoah 时建议适当增加堆内存例如增加 15%-20%以抵消其额外的内存开销并为并发操作提供缓冲避免因内存分配压力导致性能波动。3.4 第四步生产环境灰度与监控经过测试环境验证后在生产环境进行灰度发布。分批发布先在一小部分实例上启用新 JDK 和新 GC 配置。强化监控必须配置完善的监控至少包括JVM GC 监控如通过 Micrometer Prometheus Grafana。应用性能监控APM追踪关键接口的响应时间分布。系统监控CPU、内存、负载。观察核心指标GC 停顿时间jvm_gc_pause_seconds_max,jvm_gc_pause_seconds分位数。吞吐量/错误率。系统资源使用率是否异常。制定回滚方案一旦监控到关键指标恶化如 P99 延迟飙升、错误率增加能快速切回原有版本。4. 决策框架一张表帮你做出最终选择面对多个选择你可以根据以下框架快速决策你的应用特征 / 需求优先推荐备选方案关键理由与注意事项从 JDK 8 升级无特殊延迟要求G1 (默认)Parallel GCG1 是 JDK 17 默认且平衡的选择升级风险最低通常能获得比 JDK 8 Parallel/CMS 更好的停顿表现。批处理、计算密集型吞吐量优先Parallel GCG1Parallel GC 为吞吐量优化在延迟不敏感的场景下能最大化资源利用率。对延迟敏感要求 P99 200msG1ZGC/Shenandoah首先调优 G1 的-XX:MaxGCPauseMillis。多数 Web 服务在此目标下G1 已足够优秀且稳定。对延迟极度敏感要求 P99.9 10ms或堆内存 32GBZGC 或 ShenandoahG1 (需精心调优)超大堆或微秒级停顿要求是低延迟 GC 的主场。需接受一定的吞吐量开销和更高内存占用。资源极度受限内存 512MBSerial GCN/A轻量级选择避免多线程 GC 的开销。尚未进行充分测试G1 (默认)N/A永远不要在未测试的情况下将生产环境直接切换到 ZGC/Shenandoah。G1 是安全的起点。最后记住一个原则没有“最好”的垃圾回收器只有“最适合”你当前应用负载和 SLA 要求的那个。升级 JDK 17 是一次提升应用现代化水平的好机会而正确地选择和配置 GC是确保这次升级平稳、高效的关键一步。不要停留在更换版本号深入理解这些变化才能让新版本的价值真正落地。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价