温馨提示×

CentOS Tomcat日志中GC问题怎么解决

小樊
32
2025-12-11 17:34:41
栏目: 智能运维

CentOS 上 Tomcat 出现 GC 问题的定位与解决


一 快速定位与监控

  • 开启并滚动 GC 日志(推荐写入 Tomcat 日志目录,便于集中管理)
    • $CATALINA_HOME/bin/ 创建或编辑 setenv.sh,加入如下内容(按你的 JDK 版本选择其一):
      • JDK 8(经典日志):
        export CATALINA_OPTS="\
        -Xms2g -Xmx2g \
        -XX:+PrintGCTimeStamps -XX:+PrintGCDetails \
        -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime \
        -XX:+PrintHeapAtGC \
        -Xloggc:/var/log/tomcat/gc-$(date +%F).log"
        
      • JDK 9+(统一日志,推荐):
        export CATALINA_OPTS="\
        -Xms2g -Xmx2g \
        -Xlog:gc*:file=/var/log/tomcat/gc-%t.log:time,uptime,level,tags:filecount=10,filesize=10m"
        
      • 说明:日志目录需可写(如 /var/log/tomcat/),并建议按天切割,便于分析。
    • 配置 logrotate 切割 GC 日志(/etc/logrotate.d/tomcat-gc):
      /var/log/tomcat/gc-*.log {
        daily
        rotate 30
        missingok
        compress
        copytruncate
      }
      
  • 实时监控与排查
    • 查看进程:ps -ef | grep tomcat;获取 PID
    • 观察 GC 概况:jstat -gcutil 1000(关注 YGC/YGCT、FGC/FGCT、O(老年代使用率))。
    • 堆与对象:jmap -heap ;jmap -histo:live (必要时 jmap -dump:live,format=b,file=heap.hprof 导出后用 MAT 分析)。
    • 线程与锁:jstack | grep -A 20 BLOCKED(定位阻塞与竞争)。

二 常见症状与对应处理

  • 症状 1:Young GC 每分钟上百次、接口抖动明显

    • 典型原因:新生代过小,短期对象很快晋升老年代,引发频繁 Full GC。
    • 处理要点:增大新生代(如 -Xmn 或 -XX:NewRatio),让对象在年轻代尽量回收;案例显示把新生代从 256MB 提升到约 600MB 后,Old 增长变慢、Full GC 显著减少。
  • 症状 2:CMS 的 Remark 阶段 STW 过长(秒级卡顿)

    • 处理要点:在 Remark 前主动触发一次 Minor GC,减少需扫描对象;启用可中断预清理;必要时提高并行线程数。
    • 参考参数(JDK 8 + CMS):
      -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
      -XX:+CMSParallelRemarkEnabled
      -XX:CMSInitiatingOccupancyFraction=70
      -XX:+CMSScavengeBeforeRemark
      
    • 说明:CMS 已进入维护模式,若条件允许,优先迁移到 G1/ZGC
  • 症状 3:日志出现 “GC overhead limit exceeded”

    • 含义:JVM 长时间 GC 却只回收极少内存(>98% 时间回收 <2% 堆),通常是内存不足或对象晋升过快。
    • 处理要点:先查根因(泄漏/晋升过快/配置过小),不要仅用开关掩盖问题;临时过渡可用 -XX:-UseGCOverheadLimit,并配合 -XX:+DisableExplicitGC 禁止业务代码 System.gc() 触发;根本方案仍是调大堆或优化对象生命周期。
  • 症状 4:周期性卡顿(如每周固定时段)

    • 排查思路:优先核对该时段是否有 定时任务/报表/备份/缓存预热 等批处理集中执行,再结合 GC、线程、数据库慢查询综合判断;必要时将批处理错峰或限流。

三 参数模板与落地步骤

  • 基线模板(按场景选用,先备份再变更,逐步迭代)
    • JDK 8 + CMS(兼容性好,注意 CMS 维护状态)
      CATALINA_OPTS="\
      -Xms4g -Xmx4g \
      -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
      -XX:+UseParNewGC -XX:+UseConcMarkSweepGC \
      -XX:CMSInitiatingOccupancyFraction=70 \
      -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark \
      -XX:+PrintGCTimeStamps -XX:+PrintGCDetails \
      -XX:+PrintGCApplicationStoppedTime \
      -Xloggc:/var/log/tomcat/gc-$(date +%F).log"
      
    • JDK 9+(推荐 G1,适合大堆与低停顿诉求)
      CATALINA_OPTS="\
      -Xms4g -Xmx4g \
      -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m \
      -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
      -XX:InitiatingHeapOccupancyPercent=70 \
      -Xlog:gc*:file=/var/log/tomcat/gc-%t.log:time,uptime,level,tags:filecount=10,filesize=10m"
      
  • 落地步骤
    1. 采集 1–2 天稳定期 GC 日志与 jstat 数据,确定目标(如 FGC ≤ 1 次/小时、单次 STW < 200ms)。
    2. 依据“活跃数据”设定堆与新生代:稳定后做一次 Full GC,取老年代占用作为活跃数据 S;初始总堆可试 S×3~4,新生代 S×1~1.5,再按观测微调。
    3. 只变更一台实例 A/B 对比,观察 YGC/FGC 频率与耗时、STW、吞吐量、错误率,确认收益后再全量发布。
    4. 若发现对象生命周期异常或泄漏,结合 heap dump 与代码审查治理根因,参数优化只是缓解手段。

四 实用工具与排错清单

  • 日志分析:GCViewer、GCHisto、GCLogViewer、HPjmeter、garbagecat(对比不同日志、看吞吐与停顿趋势)。
  • 运行时诊断:jstat -gcutil、jmap -heap/histo/dump、jstack、VisualVM/VisualGC(远程 JMX 可选)。
  • 快速检查清单
    • GC 日志是否开启且按天滚动;目录与权限是否正确。
    • 是否存在业务代码 System.gc()(可用 -XX:+DisableExplicitGC 规避,但应先排查调用来源)。
    • 老年代是否持续增长且 Full GC 后回收效果差(疑似泄漏或晋升过快)。
    • 线程是否大量 BLOCKED(同步锁竞争/数据库/外部依赖瓶颈)。
    • 是否存在定时批处理在固定时段放大 GC 与数据库压力。

0