跳转到内容

内存泄漏排查

来自代码酷
Admin留言 | 贡献2025年5月12日 (一) 00:26的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

模板:Note

内存泄漏排查[编辑 | 编辑源代码]

内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因未能被正确释放,导致系统内存被无效占用,最终可能引发内存溢出(OOM)或性能下降。在JVM中,内存泄漏通常表现为对象不再被使用但仍被GC Roots引用,无法被垃圾回收器回收。

核心原理[编辑 | 编辑源代码]

内存泄漏的本质是可达性分析失效。根据JVM规范,对象被判定为可回收需满足: Objectunreachable Path from GC Roots to Object

典型泄漏场景包括:

  • 静态集合长期持有对象引用
  • 未关闭的资源(如数据库连接、文件流)
  • 监听器未注销
  • 线程池未清理

graph LR A[GC Roots] --> B[静态变量] A --> C[活动线程栈帧] A --> D[JNI引用] B --> E[泄漏对象] C --> F[泄漏对象]

排查工具[编辑 | 编辑源代码]

常用JVM内存分析工具
工具 作用 适用场景
jmap 生成堆转储快照 离线分析
jvisualvm 可视化内存监控 实时观察
Eclipse MAT 内存分析 深度诊断
Arthas 在线诊断 生产环境

基础排查步骤[编辑 | 编辑源代码]

1. 使用jps获取目标进程ID 2. 生成堆转储文件:

   jmap -dump:format=b,file=heap.hprof [pid]

3. 使用MAT分析.hprof文件

代码示例[编辑 | 编辑源代码]

典型静态集合泄漏案例:

public class LeakExample {
    private static List<byte[]> cache = new ArrayList<>();

    public void addToCache() {
        // 每次添加1MB数据且永不释放
        cache.add(new byte[1024 * 1024]);
    }
}

输出表现

java.lang.OutOfMemoryError: Java heap space
    at LeakExample.addToCache(LeakExample.java:6)

高级分析技巧[编辑 | 编辑源代码]

支配树分析[编辑 | 编辑源代码]

在Eclipse MAT中使用Dominator Tree视图: 1. 查找占用内存最大的对象 2. 检查其引用链 3. 定位非必要引用

引用链追踪[编辑 | 编辑源代码]

关键引用类型:

  • 强引用:直接导致泄漏
  • 软/弱引用:通常合规
  • 虚引用:不影响回收

pie title 内存泄漏引用类型占比 "强引用" : 78 "软引用" : 12 "弱引用" : 8 "虚引用" : 2

生产环境案例[编辑 | 编辑源代码]

案例背景:某电商系统每日凌晨出现OOM,重启后恢复。

排查过程: 1. 添加JVM参数收集OOM时的堆转储:

   -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp

2. MAT分析显示OrderProcessor类中的ConcurrentHashMap持续增长 3. 最终发现是未清理已完成订单的引用

修复方案

// 原错误代码
completedOrders.put(orderId, order);

// 修正后
completedOrders.remove(orderId); // 处理完成后立即移除

预防措施[编辑 | 编辑源代码]

  • 定期进行代码审查,重点关注:
 ** 集合使用情况
 ** 资源关闭操作
 ** 监听器注销
  • 使用工具自动化检测:
 ** FindBugs/SpotBugs静态分析
 ** JaCoCo覆盖率检查资源释放路径

页面模块:Message box/ambox.css没有内容。

参见[编辑 | 编辑源代码]