优秀的编程知识分享平台

网站首页 > 技术文章 正文

10年Java老兵OOM救火实录:内存泄漏排查破局秘籍

nanyue 2025-03-02 18:18:26 技术文章 5 ℃

一、惊魂午夜:一场价值300万的OOM生产事故

"王工!核心交易系统OOM了!客户无法下单!" 凌晨2点23分,我被急促的报警电话惊醒。监控大屏显示:某电商平台大促期间,订单服务堆内存持续飙升,Full GC频率从5分钟1次激增到每秒3次,最终导致整个集群雪崩。

当我颤抖着手连上跳板机时,看到的是这样的死亡日志:

复制

java.lang.OutOfMemoryError: Java heap space
Dumping heap to /app/logs/java_pid12345.hprof...

二、九阴真经:OOM排查七步绝杀技

第一步:现场保护(黄金5分钟)

bash

# 立即保存现场(容器环境同样适用)
jcmd  GC.heap_dump /tmp/heap_dump.hprof
jstack  > /tmp/thread_dump.txt
jstat -gcutil  1000 10 > /tmp/gc.log

第二步:内存快照分析(MAT实战)

  1. 使用Eclipse Memory Analyzer加载hprof文件
  2. 重点查看Dominator Tree和Leak Suspects
  3. 发现可疑对象:30万个OrderDTO实例,每个含2MB的图片BASE64编码第三步:线程堆栈关联分析

java

"Order-ThreadPool-15" #215 daemon prio=5 os_prio=0 tid=0x00007f1d7823e800 nid=0x5f21 waiting on condition [0x00007f1d4a1e7000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006a0b8a1d8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
        at java.util.concurrent.ArrayBlockingQueue.poll(ArrayBlockingQueue.java:418)
        at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:87)
        at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:31)

第四步:GC日志反推(G1GC日志解密)

[Eden: 159.0M(159.0M)->0.0B(301.0M) 
Survivors: 13.0M->13.0M 
Heap: 315.2M(1024.0M)->287.1M(1024.0M)]
[Times: user=0.23 sys=0.01, real=0.02 secs]

异常特征:每次GC后堆内存下降不足5%,且老年代持续增长

第五步:线上Arthas实时诊断

bash

# 动态监控对象创建
watch com.example.OrderService createOrder '{params,returnObj,throwExp}' -n 5 -x 3

# 方法执行跟踪
trace com.alibaba.fastjson.JSON parseObject -n 3

第六步:代码显微镜(罪魁祸首浮出水面)

java

// 致命代码:大对象缓存未清理
public class OrderCache {
    private static final Map CACHE = new ConcurrentHashMap<>();
    
    public void cacheOrder(OrderDTO order) {
        CACHE.put(order.getId(), order); // 百万级订单持续累积
    }
    
    // 无过期清理机制!!
}

第七步:压测复现(确保根治)

使用JMeter模拟高峰流量:

xml


    500
    60
    forever

运行 HTML

配合APM工具观测内存分配速率(Allocation Rate)

三、九阳神功:OOM防御六大铁律

  1. 对象池化:使用Apache Commons Pool管理大对象
  2. 缓存控制:Guava Cache设置软引用+权重淘汰
  3. Cache<Long, OrderDTO> cache = CacheBuilder.newBuilder() .softValues() .maximumWeight(100000) .weigher((k,v) -> v.getSize()) .build();
  4. 堆外内存:Netty的ByteBuf使用DirectBuffer
  5. JVM参数优化
  6. -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:InitiatingHeapOccupancyPercent=35 -XX:+AlwaysPreTouch
  7. Docker化部署:严格限制内存上限
  8. resources: limits: memory: "4Gi" requests: memory: "3Gi"
  9. 监控三板斧
  10. Prometheus + Grafana 实时监控堆内存
  11. ELK收集GC日志
  12. SkyWalking跟踪对象分配

四、涅槃重生:从OOM到架构升级

事故后我们实现了:

  1. 全链路压测平台
  2. 智能内存分析系统(自动解析hprof)
  3. 线上热修复能力(无需重启修复内存泄漏)

最后送给所有程序员的箴言:OOM不是终点,而是蜕变的起点!关注+转发本文,私信领取《Java内存问题排查红宝书》(含50个真实案例)。下期揭秘《百万级TPS系统的GC调优秘籍》——一个让JVM暂停时间归零的黑科技!

最近发表
标签列表