OOM异常实验

Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人想出来。

堆溢出

堆中存储的是对象的实例,只要不断的创建对象,并保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,当对象数量达到最大堆的通量限制后就会产生内存溢出异常。本次测试中通过限制堆内存的大小且将其限制为不可扩展(将堆的最小值-Xms和最大值-Xmx参数设置为一样)来进行堆内存溢出测试。

测试使用的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
public class HeapOOM {

static class OOMObject{}

public static void main(String[] args){
List<OOMObject> list = new ArrayList<OOMObject>();

while(true){
list.add(new OOMObject());
}
}
}

使用的VM Args:-Xms50m -Xmx50m -XX:+HeapDumpOnOutOfMemoryError;我使用的IDEA通过Edit Configurations在VM options对虚拟机参数进行设置。
虚拟机参数设置

在测试过程中发现一个非常有趣的问题,当将堆内存大小限制为50m、80m时(是否还有其他的数值会导致该现象就没有具体去深究了),运行的程序会被一直Full GC导致Stop-the-world从而导致程序一直不会被执行结束,而且发现当前使用的是Parallel Scavenge收集器。更有趣的是当使用其他的收集器时不会出现这样的问题,目前还未找到具体的原因。

堆OOM异常GC详情

正常情况下Java堆内存溢出时,溢出堆栈信息java.lang.OutOfMemoryError: Java heap space如下图:

堆OOM异常运行结果

栈溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JavaVMStackSOF {

private int stackLength = 1;

public void stackLack(){
stackLength++;
stackLack();
}

public static void main(String[] args) throws Exception {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLack();
} catch (Exception e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}

方法区和运行时常量池溢出

本地直接内存溢出