记一次JVM内存泄漏问题排查、分析过程

本文详述了一次使用SpringBoot + Akka框架的项目在JDK 1.8环境下遇到的内存泄漏问题。通过jmeter压测,配合jconsole、jvisualvm、ha450.jar和mat工具进行监控和分析,发现在增大堆内存后仍出现GCOverheadLimitExceeded异常。通过对比不同时间点的dump文件,定位到Akka流的使用问题。分析过程中发现多个内存泄漏点,包括未释放的对象、过多线程和监听器等问题。解决问题的方法包括修改代码、手动释放强引用对象以及正确使用线程池。文章强调了内存泄漏的多种原因,并提供了获取dump文件和启动jmeter的命令。

背景

项目使用框架为springboot + akka(想要了解akka框架的同学可自行查看),JDK版本1.8 使用的垃圾回收器为1.8默认的UseParallelGC

工具说明

  1. 压测工具使用的是jmeter脚本
  2. 性能监测工具使用的是jdk自带工具,jconsole、jvisualvm,一般第一个基本可以满足监测需求
    在这里插入图片描述
  3. 分析工具使用的是IBM的工具,ha450.jar(分析dump文件)、jca450.jar(分析线程信息)
    分析dump文件也可以使用 JProfile和mat(两个都为可视化分析工具)

使用相关命令说明

  1. 获取dump文件命令
    (1) 自己手动获取使用命令 jmap -dump:format=b,file=dump18947_1.hprof 18947,pid替换为自己服务pid即可。
    (2) 在服务启动命令添加jvm参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dev/jvm_log,在服务发生内存泄漏时会自动生成dump文件在指定目录下。
  2. 获取线程信息命令
    使用 jstack 命令获取,如 jstack pid > jstack.log

内存泄漏分析过程

  1. 首次压测 设置堆大小为2G,压测几个小时发现服务出现 java.lang.OutOfMemoryError: GC overhead limit exceeded 异常,首次认为是堆内存设置太小(压测服务本身比较吃资源所以有此猜测,是否因为参数问题要视项目具体情况而言),然后重新设置堆大小为 8G后重新压测,发现老年堆持续上涨且GC后并不能释放,所以定位为代码问题。
    在这里插入图片描述
  2. 因要在本地分析,dump文件较大时使用分析工具无法打开,所以重新设置堆大小为2G重新进行压测。使用jmap命令分别在压测开始的10min、1h、内存溢出时获取dump文件进行对比分析,这样能够很直观的看出来哪些资源快速增长且未释放。
  3. 这里我使用的分析工具为 ha450.jar和mat,下面使用mat进行说明
    在这里插入图片描述
    在这里插入图片描述
    这里的可视化工具会把问题给暴露出来,dump两个文件对比可以明显的发现是akka 流使用时候的问题,具体情况还需要再次进行分析。
  4. 查看具体问题
    在这里插入图片描述
    这里可以给我们更详细的信息,当我们选择点击进去查看时可以发现问题
    在这里插入图片描述
    这时候我们去自己的代码中去追溯,可以发现一些对象没有释放,一直被占用。
  5. 问题处理
    有时候内存泄漏点可能不止一个,这个需要我们多次压测并进行分析,我这里只是讲分析的一个思路。我在压测的过程中就是发现了多个泄漏点,逐一解决后压测内存才恢复正常。
    出现内存泄漏的原因有很多,如:
    (1) 对象创建较多且一直被引用导致gc不能回收
    (2) 创建线程过多导致的内存溢出
    (3) 一些监听器的使用
    (4) 静态集合类引起的内存泄漏等

在解决这类问题的时候,我们可以先找到有问题的代码进行修改,然后对一些强引用对象可以尝试手动置空释放。如果使用线程池要正确的去使用,并对一些资源进行回收释放。

共勉~

新增:

  1. dump当前进程文件命令:jmap -dump:format=b,file=文件名称.hprof 进程号;
  2. jmeter启动命令: nohup ./apache-jmeter-4.0/bin/jmeter.sh -n -t test.jmx -l result.jtl &
  3. jar启动参数:nohup java -Xmx8g -Xms8g -XX:+UnlockExperimentalVMOptions -Dfile.encoding=UTF-8 -XX:+UseG1GC -XX:MaxGCPauseMillis=10 -XX:GCPauseIntervalMillis=200 -XX:+UseStringDeduplication -XX:+ParallelRefProcEnabled -XX:+DisableExplicitGC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dev/jvm_log -Djava.rmi.server.hostname=172.31.184.209 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=10099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar service-3.1-SNAPSHOT.jar &
### JVM 内存泄漏排查方法与工具 JVM 内存泄漏是指程序在运行过程中,对象不再被使用但无法被垃圾回收器(GC)回收,导致内存资源被无效占用,最终可能引发 `OutOfMemoryError`。排查此类问题需要结合理论分析工具辅助,以下是一些常用的排查方法和工具。 #### 1. 常见内存泄漏场景 - **集合类未释放引用**:如 `HashMap`、`ArrayList` 等集合类中保存了对象引用,使用完后未清空,导致 GC 无法回收。 - **监听器和回调未注销**:如事件监听器、定时任务等未及时取消注册。 - **缓存未清理**:长时间未访问的缓存对象未被清除,持续占用内存。 - **线程未终止**:线程未正确关闭,线程局部变量(`ThreadLocal`)未清理,导致内存泄漏。 #### 2. 内存查看与分析命令 JDK 提供了多种命令行工具用于查看 JVM 内存状态和堆转储信息: - **jstat**:用于监控 JVM 堆内存和垃圾回收情况。例如: ```bash jstat -gc <pid> 1000 ``` 该命令每秒输出一次 GC 统计信息,帮助判断内存回收是否正常进行。 - **jmap**:生成堆转储快照(heap dump),用于后续分析内存使用情况。例如: ```bash jmap -dump:live,format=b,file=heapdump.hprof <pid> ``` - **jcmd**:用于执行 JVM 命令,包括查看 Native Memory Tracking(NMT)信息。例如: ```bash jcmd <pid> VM.native_memory summary ``` 该命令可查看 JVM 原生内存使用情况,帮助识别非堆内存泄漏[^4]。 #### 3. 图形化分析工具 - **VisualVM**:集成了多种监控和分析功能,支持查看堆内存、线程状态、GC 情况,并可加载 heap dump 进行对象分析。 - **Eclipse MAT(Memory Analyzer)**:专门用于分析 heap dump 文件,可识别内存泄漏的潜在对象,提供支配树(Dominator Tree)和直方图(Histogram)视图。 - **JProfiler**:商业工具,提供更强大的性能分析功能,支持实时监控内存分配、GC 情况,并可进行线程和锁分析。 #### 4. 分析步骤示例 1. **监控内存使用情况**:使用 `jstat` 或 VisualVM 监控堆内存和 GC 情况,判断是否存在频繁 Full GC。 2. **生成堆转储文件**:使用 `jmap` 生成 heap dump。 3. **加载并分析 heap dump**:使用 Eclipse MAT 或 VisualVM 加载 dump 文件,查找内存占用最高的类及其引用链。 4. **定位泄漏点**:通过支配树或直方图视图,找到未被释放的对象及其持有者,分析引用关系,判断是否为内存泄漏。 #### 5. 预防措施 - 使用弱引用(`WeakHashMap`)管理缓存或监听器。 - 在对象使用完成后显式置 `null`,帮助 GC 回收。 - 定期进行内存分析,结合监控工具(如 Prometheus + Grafana)进行长期观察。 - 设置合理的 JVM 启动参数,如 `-Xmx` 和 `-Xms`,避免内存过小。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值