OutOfMemoryError(OOM)和StackOverflowError(SOF)异常总结

大纲

  • Java虚拟机运行时数据区
  • 会发生OOM和SOF异常的内存区域
    • 虚拟机栈
    • 本地方法栈
    • 方法区
    • 本机直接内存

Java虚拟机运行时数据区

OOM和SOF都是内存溢出异常,与java内存区域的关系密不可分,所以要先了解java各个内存区域

OutOfMemoryError(OOM)和StackOverflowError(SOF)异常总结_第1张图片

会发生OOM和SOF异常的内存区域

首先明确的是,程序计数器是在《java虚拟机规范中》唯一一个没有规定任何OutOfMemoryError情况的内存区域,像其它方法区、虚拟机栈、本地方法栈、堆都有可能会抛出OOM和SOF异常。下面会对各个内存区域进行细说。

​ 堆是java虚拟机所管理的内存中的最大一个区域,所有的对象实例以及数组都应该在堆上。一般情况下,堆都是可以扩展的,通过(-Xmx 和 -Xms 来设置),如果堆在分配实例内存时超过了最大的可扩展内存,就会抛出OutOfMemoryError异常。下面来模拟以下堆的OOM异常

//设置参数:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
//HeapDumpOnOutOfMemoryError可以在发生堆内存溢出的时候,抛出一个快照文件,方便事后处理
public class OutOfMemoryErrorTest {
    public static void main(String[] args) {
        ArrayList<OutOfMemoryErrorTest> list = new ArrayList<>();
        while (true){
            list.add(new OutOfMemoryErrorTest());
        }
    }
}

得到快照文件

在这里插入图片描述

下面是快照文件.hprof里的内容

OutOfMemoryError(OOM)和StackOverflowError(SOF)异常总结_第2张图片

首先我们需要确定到底是内存泄漏还是内存溢出。我们发现导致OOM的对象就是OutOfMemoryErrorTest,而这个对象是有用的,所以要解决OOM就要检查(-Xmx 和 -Xms)参数的设置,与机器内存进行对比,看看还能不能向上调整空间。如果导致OOM的对象是无用的,也就是说它本来该被垃圾回收,所以我们需要检查GC Roots,看看该对象是怎么与GC Roots发生关联才导致无法被回收的

虚拟机栈

除了JDK1版本的虚拟机,一般情况下虚拟机是不允许栈的内存动态扩展的,所以说只要线程请求的栈的深度大于虚拟机所允许的最大深度,将会抛出StackOverflowError。我们可以通过 -Xss 设置java虚拟机最大允许栈的深度。下面来模拟以下栈溢出异常

public class StackOverTest {

    private int i=1;

    public void stackOver(){
        i++;
        //线程调用一个方法就会同步创建一个栈帧压入到操作数栈里面
        stackOver();
    }

    public static void main(String[] args) {
        StackOverTest stackOverTest = new StackOverTest();
        stackOverTest.stackOver();
    }
}

OutOfMemoryError(OOM)和StackOverflowError(SOF)异常总结_第3张图片

本地方法栈

本地方法栈和虚拟机栈一样,也会在栈深度溢出或者栈扩展失败的时候分别抛出 StackOverflowError 和 OutOfMemoryError 异常

方法区

在jdk6的时候,有方法区属于永久代,可以通过 -XX:PermSize 和 -XX:MaxPermSize 来限制永久代的大小,如果超过了最大值那么就会抛出OOM异常。而在jdk7中把永久代中的字符串常量池、静态变量等移出,放到堆中,到了jdk8完全取消了永久代,而使用元空间来代替它,把jdk7中剩余的内容例如类信息等全部移到元空间中。换句话说,jdk8使用了元空间来实现方法区,而元空间内存不在虚拟机的内存中,而是在机器本地内存,但我们仍可以通过以下几个参数来控制元空间大小和垃圾回收。

  • -XX:MaxMetaspaceSize:设置元空间的最大值
  • -XX:MetaspaceSize:指定元空间的初始化大小
  • -XX:MinMetaspaceFreeRatio:设置垃圾回收后控制元空间最小的剩余量
  • -XX:MetaspaceFreeRatio:设置垃圾回收后元空间剩余容量百分比

本机直接内存

直接内存可以通过指定 -XX:MaxDirectMemorySize来指定,如果超过了该值会抛出OOM异常,我们通过unsafe类来直接分配内存,来模拟出OOM异常

//参数设置:-Xmx20M -XX:MaxDirectMemorySize=10M
public class DirectMemory {
    public static void main(String[] args) throws IllegalAccessException {
        Field declaredField = Unsafe.class.getDeclaredFields()[0];
        declaredField.setAccessible(true);
        Unsafe unsafe = (Unsafe) declaredField.get(null);
        while (true){
            unsafe.allocateMemory(1024*1024);
        }
    }
}

OutOfMemoryError(OOM)和StackOverflowError(SOF)异常总结_第4张图片

你可能感兴趣的:(java,java虚拟机,java,开发语言,后端)