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

Posted MrDeng886

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OutOfMemoryError(OOM)和StackOverflowError(SOF)异常总结相关的知识,希望对你有一定的参考价值。

Java虚拟机运行时数据区

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

会发生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里的内容

首先我们需要确定到底是内存泄漏还是内存溢出。我们发现导致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();
    }
}

本地方法栈

本地方法栈和虚拟机栈一样,也会在栈深度溢出或者栈扩展失败的时候分别抛出 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)异常总结的主要内容,如果未能解决你的问题,请参考以下文章

OOM和SOF

Throwing OutOfMemoryError“无法分配带有空闲字节的字节分配,直到OOM为止”

jvm有fullGc 为什么还会出现oom(OutOfMemoryError)

深入理解Java虚拟机03:OOM异常

JVM OOM处理

内存溢出OOM