前言
本文记录一次使用Eclipse MAT排查内存问题的案例,缘由是线上某服务OOM,排查得知jvm old区占满,但是gc了还是无法释放
实战
首先在线上服务器排查发现某应用占用了大量的内存,由一个ConcurrentHashMap对象造成的,最终分析的原因是一直往成员对象ConcurrentHashMap存对象,但是并没有释放,这些对象gc都无法回收,导致了oom
本文就模拟一次该场景进行分析下
static List<String> list1 = new ArrayList<>();
public void test() {
for (int i = 0; i < 10000000; i++) {
list1.add(new String("" + i));
}
System.out.println("list1.size() = " + list1.size());
}
伪代码如上,执行一个方法往一个成员变量list1放了1000万个String对象
假设此时list1对象太大,导致了oom
分析
分析内存问题的常见的两大工具有:
- jvisualvm:jvm1.8及以下自带的,可以分析内存文件、线程dump文件等,也可以在线查看某个运行的java程序运行情况等
- eclipse mat(Memory Analyzer),官网:https://www.eclipse.org/mat/,需自行下载,看名字就知道是专门分析内存情况的
如果要分析以上场景,推荐使用mat(因为jvisualvm里面我没找到通过对象关联到代码的地方,可自行研究下),分析内存还是MAT更擅长一点
1、首先先把堆dump到本地
先执行jps
jps
18144 Launcher
4640 RemoteMavenServer36
7156 Jps
9508 RemoteMavenServer
8120 DemoApplication
上面的DemoApplication就是我本地启动的一个SpringBoot应用,可见pid=8120
2、dump到本地
jmap -dump:format=b,file=heap.hprof 8120
此时本地就有一个heap.hprof文件,并且文件大小达到了1.5G
3、打开mat
一打开,其实mat已经帮我们分析了一把,可以看的它分析出DemoApplication类占用了总内存:615,300,824 (91.36%) bytes
上面的是打开时的一个分析结果,我们也可以看完整的,点击前面的Overview
Dominator Tree,可以查看大对象树,我们这种情况,肯定是某个类占用的内存很大
通过上面这个界面,可以很清晰的看到了,DemoApplication类里面有个list1=ArrayList,ArrayList里面有1000万个String对象
可以看的mat甚至可以定位到某个类的某个变量,这样就很方便分析内存问题了
线上应用可以配置当发生oom的时候,自动dump出一个文件,这样可以便于出现问题线上快速重启,然后再分析dump文件,只需要配置jvm参数即可:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/error/oomheapdump.hprof
另外需要说下,dump文件一般都是比较大的,一般都是好几个g,此时需要自己电脑内存大一些,如果mat打开内存不够,则需要修改mat目录下面的MemoryAnalyzer.ini文件,把里面的-Xmx参数调大就好了