概述

本文目的

  • 通过代码验证Java虚拟机规范中描述的堆中存储的内容
  • 遇到内存溢出错误时
    • 能根据异常的信息快速判断出是堆的溢出
    • 出现此类问题后如何处理

操作

创建类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.super2bai.jvm;

import java.util.ArrayList;
import java.util.List;

/**
*
* @author 2bai <br/>
* VM Args:<br/>
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails
* -XX:SurvivorRatio=8<br/>
*
* -verbose:gc<br/>
* 在虚拟机发生内存回收时在输出设备显示信息<br/>
* -Xms20M<br/>
* 堆的最小可用内存为20M<br/>
* -Xmx20M<br/>
* 堆的最大可用内存为20M<br/>
* -Xmn10M<br/>
* 表示新生代分配10M<br/>
* -XX:+PrintGCDetails<br/>
* 打印GC日志到控制台<br/>
* -XX:SurvivorRatio=8<br/>
* 表示新生代的eden区:from区:to区:8:1:1<br/>
* -XX:+HeapDumpOnOutOfMemoryError<br/>
* 让虚拟机在出现内存溢出时Dump出当前的内存转储快找以便事后分析<br/>
*
* PS:堆的最大可用内存和最小可用内存设置成相同的值,即可避免堆自动扩展<br/>
*
*
*/
public class HeapOOM {
static class OOMObject {
}

public static void main(String[] args) {
List<OOMObject> list = new ArrayList<>();
while (true) {
/**
* Java堆用来存放对象实例<br/>
* 只要不断创建对象<br/>
* 并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象<br/>
* 那么对象数量到达最大堆的容量限制之后就会产生内存溢出异常<br/>
*/
list.add(new OOMObject());
}
}
}

设置虚拟机参数

右键单击类文件 -> Run As -> Run Configurations...

1
-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError

VM Args

运行结果

堆内存溢出

运行后,如果未指定dump文件路径,会在当前项目的根路径下生成名为java_pid*.hprof的文件,如果没有此文件,F5刷新当前项目。

Java堆内存的OOM异常是实际应用中常见的内存溢出异常情况。

当出现Java堆内存溢出时,异常堆栈信息java.lang.OutOfMemoryError会跟着进一步提示Java heap space

解决

一般的手段时通过内存映像分析工具(如·Eclipse Memory Analyzer·)对Dump出来的堆转储快进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)

Eclipse Memory Analyzer

MemoryAnalyzer官网

安装使用教程

MAT使用技巧

其它

Memory Leaks and Java Code

How to find and fix memory leaks in your Java application

日后有时间将会翻译并发布上面两篇文章

重点看RednaxelaFx的回答,R大讲的不错

What is the difference between an OutOfMemoryError and a memory leak