环境
java:1.8
Eclipse Memory Analyzer:1.8.0(如果jdk是1.7,请下载相对应的版本)
前言
这篇文章 是篇基础入门篇。
了解下,当然内存发生溢出时,大概一个处理思路;
公司的行情服务器,一直以来都非常吃内存,最近同事反馈说,早上跑job
,到了下午
CPU
就彪1000%
左右;而且内存只剩下1G
左右,总内存是64G
;
使用jstat -gcutil 20845 2000 0
来分析,得知,老年代都已经是99.99
,而一次FGC
的非常长,导致服务器无法正常工作。
所以我们需要知道堆内存中的信息。更确切的说,是哪个类中的哪个变量没有释放内存。
前期准备
我们既然是要分析堆内存的信息;
很明显我们第一步就需要得到一个关于堆内存信息的文件 — 堆转储文件
eclipse
如果是eclipse
,则我们可以在Run Configuration
中的VM options
添加如下配置:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump
Tomcat
这段摘抄网上,本人没有实践,因为我公司不使用Tomcat
如果我们部署生产环境,并且使用的是Tomcat
容器,则我们需要在bin
目录下的catalina.sh
文件中,添加下面的配置:
JAVA_OPTS="$JAVA_OPTS -server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=~/heapdump"
其中,-server
告诉tomcat使用server模式 能获得更大并发数和性能。
java自带的命令 jmap
拿我公司的项目举例,我公司项目使用的是play framework
框架;
所以我获取堆转储文件的命令是:
jmap -dump:format=b,file=<dumpfile.hprof> <pid>
//实践 事实上线上环境都可以使用该命令来获取。
jmap -dump:format=b,file=head.bin 3409
构造产生OOM的代码
假设我们自己测试,或者说,你并么有遇到内存泄漏的情况;
我们可以自己来模拟出来;也就是写段程序,让其产生内存泄漏
下面这段代码摘抄之网上,就是在一个web,程序中不断请求,而请求的内容会使其产生大量的对象,并且没有释放。
@Controller
@RequestMapping("/oom")
public class OOMTest {
private ArrayList<byte[]> memoryLeakArray = new ArrayList<byte[]>();
private ArrayList<byte[]> anotherArray = new ArrayList<>();
@RequestMapping("test.do")
public void oomTestAction(HttpServletResponse response) {
System.out.println("OOM Test~~~~~~~~");
// 存放小对象的List
anotherArray.add(new byte[1024]);
for (int i = 0; i < 1024; i++) {
byte[] tmp = new byte[1024 * 1024]; // 添加1M的数据到List中
memoryLeakArray.add(tmp); //warning: 这里会造成OOM
}
}
}
这里我用了两个ArrayList,其中memoryLeakArray会存放较大的对象,每次请求进来都会往List中放1G的数据,而anotherArray只存放一些较小的对象。启动服务后,在浏览器中访问这个Controller,一般4次之内就能看到JVM抛出OOM异常了。
使用MAT工具分析
安装方法:
1、在eclipse
中进行安装:
①Help->Eclipse Marketplace
②搜索MAT
,安装Eclipse Memory Analyzer
,然后重启就可以了。
如何选择堆转储文件
就可以了。
2、不使用eclipse
插件,而是使用其独立的版本:
Memory Analyzer是可以单独使用的:
下载地址:http://www.eclipse.org/mat/downloads.php
window版解压后:
分析堆转储文件
路径:File --> Open Heap Dump
再选择生成的堆转储文件如上面写的heapdump
文件,然后就会看到如下一个分析报告图:
点击图中的Dominator Tree可以按照对象的大小来展示一个如下的层级结构图,方便我们快速找到那些大对象,它们是最有可能造成内存泄漏的地方:
我们再将其展开:
我们选择with incoming references
,然后我们会在一个新的详情页面看到这个ArrayList
属于哪个类,名字叫什么:
可以看到,这个正是之前疯狂插入大对象的那个List,找到了这个对象,就可以在代码中查找使用它的地方并定位有问题的代码了。除了通过Overview页面的Dominator Tree查找原因以外,MAT还会生成报告,查找可疑的内存泄漏点。点击Dominator Tree右边的Leak Suspects,可以看到下面这个分析报告,MAT也同样帮我们找到了造成OOM的可疑内存对象:
至此基本完毕;
下面我贴出,我公司服务器实际生产环境中的情况:
由于家里看不到公司的代码,这个问题还要等明天去公司里才能确定。
总结
1、使用jmap
命令来获取堆转储文件
jmap -dump:format=b,file=<dumpfile.hprof> <pid>
如果只是在本机上测试,则只需要修改的eclipse
中Run Configuration
的VM options
:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:\heapdump
如果生成的堆转储文件比较大,则需要先从服务器上下载下来;
再到一台内存足够的本机上,使用mat进行分析
当然也可以下载linux版的MAT,直接到服务器上进行分析:即执行解压包里的脚本即可。
只是服务器上没有图文并茂的说明,所以还是建议下载下来进行分析
2、使用Memory Analyzer
进行分析;
如果生成的堆转储文件非常大,比如我公司服务器上生成的就是5.4G
大小的文件,
那么就需要调整Memory Analyzer
的内存大小;
即找到MemoryAnalyzer.ini
文件:
修改-Xmx1024m
参数,比如我就是=改为-Xmx6144m
;
参考地址:
Java OOM 分析Demo