JVM - 垃圾回收调优 (GC Tuning)

JVM 垃圾回收调优 (GC Tuning)

GC调优是高级Java工程师必须具备的核心技能之一。它不是一个“一招鲜”的技术,而是一个需要明确目标、分析日志、不断实验的系统性工程。

1. GC 调优的核心指标(目标)

在开始调优之前,必须明确你的业务场景和调优目标。GC调优主要关注三大核心指标,它们之间通常存在“跷跷板效应”,难以同时达到最优。

  1. 吞吐量 (Throughput)

    • 定义:CPU用于执行用户代码的时间占总运行时间的比例。即 用户代码执行时间 / (用户代码执行时间 + GC时间)
    • 适用场景:追求在长时间内完成尽可能多的工作,对单次停顿不敏感的后台计算密集型任务(如大数据批处理、科学计算、报表生成)。
  2. 停顿时间 (Pause Time)

    • 定义:垃圾收集器工作时,暂停所有用户线程("Stop-The-World", STW)的时长。
    • 适用场景:对响应时间非常敏感的与用户交互的在线服务(如Web网站、API服务、交易系统)。低停顿时间意味着更流畅的用户体验。
  3. 内存占用 (Footprint)

    • 定义:Java堆所占用的内存大小。
    • 适用场景:对内存资源有限制的场景(如嵌入式设备、高密度部署的微服务)。

总结:调优前必须做出取舍。后台任务优先保证吞吐量;在线服务优先保证低停顿时间

2. 以“降低停顿时间”为目标的调优思路

这是在线服务最常见的调优目标。

第一步:选择合适的收集器

  • 在现代Java版本(JDK 8u40+)和有足够内存(>6G)的服务器上,G1收集器通常是首选
    • 开启G1-XX:+UseG1GC (含义: 显式开启G1垃圾收集器)

第二步:设定明确的停顿时间目标

  • 这是G1调优的核心。通过设置一个期望的最大停顿时间,让G1去自动调整内部参数来达成这个目标。
  • 常用参数-XX:MaxGCPauseMillis=200 (含义: 设置G1收集器单次STW停顿时间的最大期望值为200毫秒。这是一个“软目标”,G1会尽力达成,但不100%保证。)
    • 注意:此值不宜设置得过小(如<50ms),否则可能导致GC过于频繁,吞吐量急剧下降。

第三步:开启并分析GC日志

GC调优离不开GC日志,它是调优的“眼睛”。

  • 开启GC日志 (JDK 9+)-Xlog:gc*:file=gc.log:time,level,tags:filecount=5,filesize=10m
    • 参数解释:
      • gc*: 记录所有GC相关的日志信息。
      • file=gc.log: 将日志输出到当前目录下的 gc.log 文件。
      • time,level,tags: 定义日志内容的格式,包含时间戳、日志级别和标签。
      • filecount=5,filesize=10m: 启用日志滚动。最多保留5个日志文件,每个文件最大为10MB。
  • 通过分析日志,我们可以看到GC的频率、耗时、原因等,然后进行针对性调整。

第四步:进行针对性调整

  • 调整新生代大小

    • 很多STW停顿由新生代的Minor GC引起。如果新生代太小,会导致频繁的Minor GC。
    • 常用参数
      • -Xmn: (含义: 直接设置新生代的绝对大小,优先级高于 -XX:NewRatio)
      • -XX:NewRatio: (含义: 设置老年代与新生代的内存大小比例)
      • -XX:SurvivorRatio: (含义: 设置新生代内部Eden区与单个Survivor区的大小比例)
    • 思路:观察Minor GC频率和耗时,尝试增大新生代(如 -Xmn4g),看是否能有效降低GC频率,同时单次停顿时间仍在可接受范围内。
  • 调整对象晋升阈值

    • 常用参数-XX:MaxTenuringThreshold=15 (含义: 对象在新生代Survivor区最多经历15次Minor GC后晋升到老年代)
    • 思路:如果发现大量生命周期不长的对象过早进入老年代,导致频繁的Full GC/Mixed GC,可以尝试调高此阈值,让对象在新生代多停留一段时间。
  • 调整G1的老年代GC触发时机

    • 常用参数-XX:InitiatingHeapOccupancyPercent=45 (含义: 当老年代已用堆内存的百分比达到45%时,触发G1并发标记周期)
    • 思路:当老年代使用率达到此阈值时,G1会启动并发标记周期。如果GC日志显示堆空间耗尽前未能完成老年代的回收(导致Full GC),可以尝试降低此值,让G1更早地开始回收老年代。

总结调优思路:

  1. 选择G1收集器。
  2. 设置一个合理的停顿时间目标 (MaxGCPauseMillis)。
  3. 打开GC日志进行监控。
  4. 分析日志,调整新生代大小 (-Xmn) 来平衡Minor GC的频率和单次耗时。
  5. 如果老年代GC有问题,调整触发阈值 (InitiatingHeapOccupancyPercent) 或 对象晋升阈值 (MaxTenuringThreshold)。
posted @ 2026-01-21 16:07  我是刘瘦瘦  阅读(1)  评论(0)    收藏  举报