Tomcat服务停滞问题分析:内存泄漏导致GC抖动及线程阻塞(内存泄漏导致的渐进式服务退化)

问题排查报告

问题背景
  • 发生时间:2023-07-15 上午
  • 现象
    Tomcat进程持续运行,但无法响应请求(假死),持续约20分钟。
    系统访问卡顿,网页无法加载。

系统资源排查

通过以下命令检查基础资源占用:

top       # CPU监控(发现异常进程7020)
free -h   # 内存监控
df -hl    # 磁盘监控
PID   USER  %CPU  COMMAND
23328 root  399.6  java
关键发现
  1. CPU占用近400%+
    通过top -Hp 23328定位异常线程:
    在这里插入图片描述
    ## 使用过命令强制输出23328的堆栈信息从而分析出可能出现内存泄露的代码(-F:强制执行,CPU打满的情		   况下一定要强制输出,不然执行很慢甚至可能无法得到执行结果)
    jstack -F 23328 > thread_dump_F.txt
    
  2. JVM内存占用99%
    jmap -heap输出显示内存耗尽:
    PSYoungGen used 1332687K/1396736K (99%)  // 年轻代
    ParOldGen used 5401292K/5401600K (99%)   // 老年代
    

根因分析过程
  1. 堆内存快照分析
    紧急生成Heap dump:

    jmap -dump:live,format=b,file=heapdump.hprof 7020
    
    • 使用MAT/Eclipse分析发现:
      对象XXXX的实例数异常增多(内存泄漏)
    • 泄漏路径:
      XXXX对象 → 被本地缓存强引用 → 无法被GC回收
  2. 线程栈分析
    jstack -F输出显示:

    • Tomcat所有200个工作线程均处于 waiting for monitor entry(锁竞争)
    • 线程阻塞原因为:频繁Full GC导致系统卡顿,形成恶性循环:
      内存泄漏
      老年代占满
      频繁Full GC
      线程停顿/阻塞
      新请求堆积

问题总结
关键问题根本原因导致后果
内存泄漏本地缓存强引用XXXX对象不释放老年代持续占满
线程阻塞Full GC导致系统停顿Tomcat线程池耗尽
假死现象GC线程与业务线程资源竞争请求堆积无响应

为什么未触发OOM?

  • 内存泄漏是渐进过程,当泄漏速度 ≈ GC回收速度时:
    Full GC持续尝试回收内存(暂停业务线程),但始终无法释放足够空间。
    此时系统进入 “卡死”状态:既未崩溃,又无法处理新请求。

修复建议
  1. 修复内存泄漏

    • 检查XXXX对象的缓存逻辑,改用弱引用或增加过期机制
    • 避免静态集合长期持有对象
  2. JVM参数优化

    -XX:+UseG1GC                     // G1收集器更适合大堆
    -XX:MaxGCPauseMillis=200         // 控制GC停顿时间
    -XX:InitiatingHeapOccupancyPercent=45  // 更早启动并发GC
    
  3. 增加监控

    • 添加Prometheus+Grafana监控堆内存/GC频率
    • 设置阈值告警(如Old Gen >80%持续5分钟)

讨论点
  • 是否遇到过类似缓慢内存泄漏导致假死的场景?
  • 对于服务中程序设计,有哪些最佳实践可避免此类问题?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值