Java内存溢出异常实例源码堆方法区虚拟机栈,JVM内存几个重要区域:堆,方法区,虚拟机栈,本地方法栈,程序计数器。出了程序计数器之外其他区域都有可能找出OutOfMemoryError,下面简称OOM。
- 堆溢出,堆用来存放Java类实例,当类实例不断创建,而垃圾由于种种原因无法进行类实例的垃圾回收,在这种情况下,堆内存就很有可能会突破其上限,这个时候就会报OOM。单元测试代码如下:
|
1
2
3
4
5
6
7
|
@Testpublic void testHeapOutOfMemoryError() { List list = new ArrayList<>(); for (;;) { list.add("test"); }} |
- 栈深度溢出,增加此方法栈中本地变量表的长度,一般出现在递归调用中,递归调用深度过大造成,这也是使用递归的潜在风险。结果会抛出StackOverflowError。下面的测试用例在windows下会造成系统假死,请谨慎运行。
|
1
2
3
4
5
6
7
8
9
|
@Testpublic void testStackException() {test(0, 1);}public int test(int i ,int j) {j = i + j;return test(i, j);} |
- 栈内存溢出,栈内存=2G – 堆内存 – 方法区内存。对于每一个线程,栈内存都是独有的,所以,如果线程数越多或者或者当个线程栈内存分配越大,都会造成栈内存溢出。
|
1
2
3
4
5
6
7
8
9
10
11
12
|
@Testpublic void testStackOutOfMemoryError() { while (true) { new Thread(new Runnable() { public void run() { while (true) { } } }).start(); }} |
- 方法区溢出,方法区大小是程序运行前指定的,如果程序载入的class,jar和动态代理或者字节码增强造成程序运行时载入超过指定的大小,就会造成OOM。次例子使用CGLib直接操作字节码运行时,生成大量的动态类。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@Testpublic void testMethdoAreaOutOfMemoryError() { while(true) { Enhancer enhance = new Enhancer(); enhance.setSuperclass(OOMObject.class); enhance.setUseCache(false); enhance.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method arg1, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); enhance.create(); }}static class OOMObject {} |
- 本机直接内存溢出,在JDK1.4之后,Java引入了NIO,其中Channel和Buffer的实现,采用了直接内存的方法,以提高IO速度,所以使用DirectByteBuffer分配内存就有可能会抛出OOM;本例子绕过了DirectByteBuffer,直接使用Unsafe分配内存。
|
1
2
3
4
5
6
7
8
9
|
@Testpublic void testDirectOutOfMemoryError() throws IllegalArgumentException, IllegalAccessException { int _1MB = 1024 *1024; Field unsafeField = Unsafe.class.getDeclaredFields()[0]; Unsafe unsafe = (Unsafe) unsafeField.get(null); while(true) { unsafe.allocateMemory(_1MB); }} |
- 运行时常量池溢出,如果要想运行时常量池添加内容,最简单的方式是使用String,inter()这个Native方法。该方法的作用是:如果常量池中已经包括一个等于此String对象的字符串,则返回代表池中的这个字符串的String对象;否则,将此对象包含的字符串增加到常量池中。
|
1
2
3
4
5
6
7
8
|
@Testpublic void testRuningConstantOutOfMemoryError() { List list = new ArrayList<>(); int i = 0; while(true) { list.add(String.valueOf(i++).intern()); }} |
