Smart's Blog

使用堆转储文件分析java内存溢出问题

序言:

       我们在运行java应用程序时,经常会遇到OutOfMemoryError的内存溢出问题,而且在代码量大逻辑复杂的系统里,要定位内存溢出的代码段是一个很让人困扰的问题。下面来看一种通过获取堆转储文件的形式来分析内存溢出的方法。

       我们先编写一个demo,创建一个名为User的实体类,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class User {
private int id;
private String name;
private String phone;
private String email;
public User(int id, String name, String phone, String email) {
this.id = id;
this.name = name;
this.phone = phone;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 堆转储测试类
* */
public class Test_Heap_Dump {
public static void main(String args[]){
User user;
List<User> list = new ArrayList<User>();
for (int i=1; i<=10000000; i++) {
user = new User(i,"test"+i+"","1234567"+i+"","email");
list.add(user);
System.out.println("size : " + i);
Runtime runtime = Runtime.getRuntime();
System.out.printf("maxMemory : %.2fM\n", runtime.maxMemory()*1.0/1024/1024);
System.out.printf("totalMemory : %.2fM\n", runtime.totalMemory()*1.0/1024/1024);
System.out.printf("freeMemory : %.2fM\n", runtime.freeMemory()*1.0/1024/1024);
}
}
}

       然后编写一个测试方法,循环创建一千万个User对象,存储于一个List内。之后设置Idea的运行选项,在VM options内加入:

-Xmx40m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/Users/yangml/Desktop

       我们这里设置了-Xmx最大堆内存大小为40m是为了减小JVM堆内存的大小,使测试方法可以比较简单的触发OutOfMemoryError。后面的两个选项是设置堆转储的关键点,-XX:+HeapDumpOnOutOfMemoryError的作用是JVM在遇到OutOfMemoryError时会拍摄一个“堆内存快照”,这个快照就是将当前JVM的堆内存,并将其转储后面HeadDumpPath指定的目录下。接着运行程序,一段时间后,抛出了OnOutOfMemoryError异常:

image

       同时,在指定目录下生成了一个java_pid29013.hprof文件,这是一个二进制文件,需要使用其他工具来打开它,这里我们使用JProfiler来打开,打开后如下图:

image

       可以看到创建了十分多的User类实例对象,并且没有被垃圾回收器回收,因此可以断定程序在某个地方创建了大量的User对象,并且没有被及时的回收。