温馨提示×

如何通过日志优化Ubuntu Tomcat的JVM参数

小樊
52
2026-01-11 12:40:50
栏目: 智能运维

通过日志优化 Ubuntu 上 Tomcat 的 JVM 参数


一 准备可观测性日志

  • 启用并落盘 GC 日志(建议统一放在 $CATALINA_BASE/logs/
    • 推荐组合(JDK 8 常用):
      • -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:$CATALINA_BASE/logs/gc.log
      • 如需观察停顿分布,可加:-XX:+PrintGCApplicationStoppedTime
    • bin/catalina.sh 的合适位置(如注释行下方)追加到 JAVA_OPTS,例如:
      • JAVA_OPTS=“$JAVA_OPTS -server -Xms2g -Xmx2g -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintGCApplicationStoppedTime”
  • 打开 Tomcat 访问日志便于关联慢请求与 GC 波动
    • conf/server.xml 内配置 AccessLogValve,输出如:%h %l %u %t “%r” %s %b %D(其中 %D 为请求耗时 ms),并按日滚动切割,便于定位 GC 前后请求延迟变化。
  • 可选:开启 JMX 便于外部监控(VisualVM/JConsole),在 JAVA_OPTS 增加:
    • -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false(生产请启用认证与加密)。

二 从日志中识别问题

  • GC 日志关键信号
    • 频繁 Full GC:常见于老年代空间不足或元空间(JDK 8 为 PermGen)不足;日志中会出现 “Full GC” 且 tenured 区占用接近上限。
    • 启动阶段短时间多次 Full GC:多见于 PermGen/Metaspace 初始过小,类加载触发 Full GC;日志中 “Perm” 或 “Metaspace” 容量打满。
    • GC 停顿过长:关注 [Times: user=…, sys=…, real=…]real 时间,若接近或超过业务可容忍阈值,需优化收集器或堆布局。
  • 辅助实时监控
    • 观察整体 GC 负载:jstat -gcutil 1000(每秒输出一次各代使用率与 GC 次数/时间)。
    • 查看堆配置与运行时分区:jmap -heap
    • 抓取线程栈定位 CPU 热点或阻塞:jstack > stack.log,结合 top -H 找高占用线程的 nid 再转 16 进制检索。
  • 可视化分析
    • gc.log 上传 GCEasy 等工具,快速得到吞吐量、最大/平均停顿、Young/Full GC 次数与原因分布,用于指导下一轮参数调整。

三 基于日志的迭代调参步骤

  • 堆大小与稳定性
    • 若 Full GC 频繁或老年代长期打满:适度增大 -Xms/-Xmx(建议等值,避免运行期扩容抖动),并观察 Full GC 是否显著减少。
    • 若 Young GC 过密且对象生命周期短:适度增大 新生代(如 -Xmn-XX:NewRatio),让短命对象多在年轻代回收。
  • 元空间(JDK 8 为 PermGen)
    • 启动或运行中频繁因元空间触发 Full GC:提高 -XX:MetaspaceSize(初始阈值)与 -XX:MaxMetaspaceSize(上限),减少因扩容触发的 GC。
  • 收集器选择与目标
    • 低延迟优先(交互/网关/API):优先 G1 GC,设置目标停顿 -XX:MaxGCPauseMillis=100~200,并配 -XX:+UseG1GC;必要时调节并发线程与 IHOP(如 -XX:InitiatingHeapOccupancyPercent)。
    • 吞吐优先(后台批处理):可选 Parallel GC(吞吐量优先,停顿不可控性更高)。
    • 传统 CMS(JDK 8 仍可用):组合 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC,并通过 -XX:CMSInitiatingOccupancyFraction 提前触发并发回收,减少并发失败退化 Full GC 的风险。
  • 线程与栈
    • 若线程数接近系统/容器上限或栈深较大:适度调整 -Xss,在可用内存与可创建线程数之间平衡。
  • 变更验证
    • 每次只变更一类参数,保留上一版 gc.log 以便对比;用 jstat/jmap 与业务指标(RT、TP99、错误率)联合评估,确认“停顿下降、吞吐提升、Full GC 减少”。

四 示例配置与落地检查

  • 示例 A(JDK 8,CMS,中等堆)
    • JAVA_OPTS=“$JAVA_OPTS -server -Xms2g -Xmx2g -Xmn768m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSCompactAtFullCollection -XX:+CMSParallelRemarkEnabled -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintGCApplicationStoppedTime”
  • 示例 B(JDK 8/11+,G1,低延迟目标)
    • JAVA_OPTS=“$JAVA_OPTS -server -Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=150 -XX:InitiatingHeapOccupancyPercent=45 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -Xloggc:$CATALINA_BASE/logs/gc.log -XX:+PrintGCApplicationStoppedTime”
  • 落地检查
    • 重启后在 $CATALINA_BASE/logs/catalina.outgc.log 确认参数已生效(如 “Command line” 能看到完整 JAVA_OPTS)。
    • jps 获取 ,执行 jstat -gcutil 1000 30jmap -heap ,核对堆与 GC 行为是否符合预期。

五 常见陷阱与修正

  • 只设 -Xmx 不设 -Xms:运行期频繁扩容会放大 GC 抖动,建议等值。
  • 过早/过晚触发并发回收(CMS):并发失败会退化 Full GC,需结合日志与负载调 CMSInitiatingOccupancyFraction
  • -Xmn 设得过大或过小:过大挤占老年代易 Full GC,过小 Young GC 过密;结合对象生命周期与 GC 日志迭代。
  • 忽视元空间:类加载多时未调 MetaspaceSize/MaxMetaspaceSize,会在启动/运行中频繁 Full GC。
  • 日志未落盘或未滚动:无法回溯分析;务必启用 -Xloggc 并做按日切割。

0