Java内存溢出异常实例源码堆方法区虚拟机栈,JVM内存几个重要区域:堆,方法区,虚拟机栈,本地方法栈,程序计数器。出了程序计数器之外其他区域都有可能找出OutOfMemoryError,下面简称OOM。
- 堆溢出,堆用来存放Java类实例,当类实例不断创建,而垃圾由于种种原因无法进行类实例的垃圾回收,在这种情况下,堆内存就很有可能会突破其上限,这个时候就会报OOM。单元测试代码如下:
1
2
3
4
5
6
7
|
@Test public void testHeapOutOfMemoryError() { List list = new ArrayList<>(); for (;;) { list.add( "test" ); } } |
- 栈深度溢出,增加此方法栈中本地变量表的长度,一般出现在递归调用中,递归调用深度过大造成,这也是使用递归的潜在风险。结果会抛出StackOverflowError。下面的测试用例在windows下会造成系统假死,请谨慎运行。
1
2
3
4
5
6
7
8
9
|
@Test public 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
|
@Test public 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
|
@Test public 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
|
@Test public 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
|
@Test public void testRuningConstantOutOfMemoryError() { List list = new ArrayList<>(); int i = 0 ; while ( true ) { list.add(String.valueOf(i++).intern()); } } |