跳转至

实战:OutOfMemoryError异常

在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生 OutOfMemoryError(下面称OOM)异常的可能,下面通过若干实例验证异常发生的场景。

1. Java堆溢出

Java堆用于存储对象实例,只要不断的创建对象,并且保证GC Roots对象之间有可达路径来避免垃圾回收机制清楚这些对象,那么在对象数量到达最大堆的容量限制后就会产生内存溢出异常。

测试代码(Java堆内存溢出异常测试):

import java.util.ArrayList;
import java.util.List;
/*
VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

-Xms20m: 设定程序启动时占用内存大小为20M
-Xmx20m:设定程序运行期间最大可占用的内存大小为20M
-XX:+HeapDumpOnOutOfMemoryError: 让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析
 */
public class HeapOOM {

    static class OOMObject{

    }

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

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

输出结果:

2. 虚拟机栈和本地方法栈溢出

由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于HotSpot来说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由-Xss参数设定。

两种异常: - 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常 - 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

测试代码:


/*
VM Args: -Xss128k
减少栈内存容量,结果抛出 StackOverflowError 异常,异常出现时输出的堆栈深度相应缩小
 */


public class JavaVMStackSOF {
    private int stackLength = 1;

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

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

输出结果:

Exception in thread "main" java.lang.StackOverflowError
    at chapter_2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
    at chapter_2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
    at chapter_2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
    at chapter_2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)

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

String.intern() 是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串中,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。


书中的jdk版本为7以下,在8以上测试用例已经失效,测试用例待补充。。。


4. 本机直接内存溢出


书中的jdk版本为7以下,在8以上测试用例已经失效,测试用例待补充。。。