您的位置:首页 > 编程语言 > Java开发

JAVA 内存溢出 分析

2017-06-16 10:42 351 查看
需要用到的工具:

1.eclips 

2.MemoryAnalyzer    http://mirror.downloadvn.com/eclipse//mat/1.6.1/rcp/MemoryAnalyzer-1.6.1.20161125-win32.win32.x86_64.zip

代码如下:

package cn.sigangjun.memoryManage;

import java.util.*;

/*
* 测试内存溢出
* VM args: -Xms20m -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
int num;

public HeapOOM() {
num = 100000;
// TODO Auto-generated constructor stub
}

public static void main(String[] args) {
Object object = System.getProperties().get("os.name");

System.out.println(object);
List<HeapOOM> list = new ArrayList<HeapOOM>();
while (true)
list.add(new HeapOOM());
}
}


运行出错,错误信息如下:

Windows 7
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid9296.hprof ...
Heap dump file created [31216829 bytes in 0.082 secs]


找到java_pid9296.hprof .文件(位于项目的的根目录)
使用MemoryAnalyzer  工具打开,具体步骤如下:











以下为引用:  http://blog.csdn.net/mhmyqn/article/details/48054219

在项目运行过程中,可能会出现内存溢出,内存溢出的原因多种多样,而在内存溢出后,我们如何查找和分析内存溢出的原因呢?这里来说一说我遇到的次遇到的内存溢出经历。

大致情况是这样的:应用在启动后,过一段时间(这个时间不确定),内存忽然爆满,然后频繁的YGC,一会过后,老年代爆满,然后是频繁的FGC,最终撑爆内存,抛出OOM。重启应用后,还是这个过程。


1、查看java进程的内存使用情况和GC情况

通过jastat工具查看GC情况,以及各generation占用比例:

[plain] view
plain copy

jstat -gcutil <pid> 1000  

通过观察分析是否是JVM的运行内存分配不合理,可通过新生代、老年代内存占用情况,以及GC情况观察得出。

如果FGC很频繁,说明老年代被迅速占用满,这有可能是老年代本身分配的内存太小,或者是内存中大量对象被引用,导致无法被YGC等等,最终这些对象都进入老年代导致老年代爆满。

要分析是什么原因造成的,可以获取内存溢出时JVM的内存快照,通过对快照的分析,来定位内存溢出的原因。

通过jmap查看JVM堆内存概况:

[plain] view
plain copy

jmap -heap <pid>  

这个内存概况会根据使用的垃圾收集器的不同而有所差异。


2、获取jvmdump

获取jvmdump可通过添加应用服务器(如tomcat)启动参数:

[plain] view
plain copy

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/mikan/jvmdump  

配置该参数后,在内存溢出时,JVM就会自动dump出当时的内存快照到HeapDumpPath指定的目录。

也可通过jmap在线获取内存dump,只是在使用jmap获取dump期间应用会被卡住,这可能对线上应用会有影响。如

[plain] view
plain copy

jmap -J-d64 -dump:format=b,file=/home/mumq/jvmdump.hprof <pid>  

其中pid为Java进程的ID。

获取到JVM内存快照后,可以使用相关的工具来对它进行分析。


3、分析jvmdump

jvmdump文件一般比较大(几个G),所以在做离线分析时,千万别在服务器上,因为在解析jvmdump时,会占用极大的内存,如果在服务器上,很可能影响线上应用的正常运行。所以一般情况是down到空闲的机器上做离线分析。

分析jvmdump的工具有很多,这里推荐使用的是Memory Analyzer (MAT),下载地址是:http://www.eclipse.org/mat/downloads.php

当前最新Release版本是1.4.0,提供了两种版本:eclipse插件和独立版本,都可通过上面链接下载。

这里以独立版本为例。在开始分析dump文件之前,先设置MAT的内存,可根据分析的dump文件大小来设置,如果分析的dump文件为2G,那设置2G内存的话,那么解析dump文件生成report会很快,否则,等几个小时都不知道。。

如何设置?在与MAT相同目录下的MemoryAnalyzer.ini文件中添加如下配置:

[plain] view
plain copy

-vmargs  

-Xms2048m  

-Xmx4096m  

具体MAT的使用方法,可以直接查看MAT自带的示例教程,相对来说还是比较全面的。

在MAT解析完内存快照后,会生成相应的report,通过report可以看到当时内存使用情况、内存溢出的可疑之处(Leak Suspects)。如:



点击Leak Suspects,可以查看到具体的详细信息:



点击detail,查看详细信息,通过 Accumulated Objects by Class in Dominator Tree列表可以看到:



User对象有10W+,怎么会这样呢?通过 Accumulated Objects in Dominator Tree列表可以看到:



是调用了UserServiceImpl.login方法,一次性查询出了10W+的用户数据。如果频繁的调用该方法,则会很快的导致OOM。再查看代码发现在调用login方法时,如果用户名参数传null时,会查询出所有用户名为null的用户。这在数据量还很小或者请求量不大的时候,可能不会有什么问题,一旦数据量上来了,或请求量上来了,那很快就会出现OOM。

找到问题原因,如何解决就容易了。所以在写代码的时候如果考虑不周全,就有可能导致这种OOM的bug。

血的教训啊。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: