1. 首页
  2. 乌拉客

OutOfMemoryError异常

我们知道在Java虚拟机内存中,除了程序计数器外,其它的内存区域都可能会发生OutOfMemoryError异常。本文将用具体的事例来演示在什么情况下会出现OutOfMemoryError异常,并以此来演示一下相关的虚拟机参数。

  • 堆溢出 

我们知道Java堆是用来存储对象实例的,只要我们不断的创建对象,并保证它们不被Java垃圾回收器回收,当存储的对象数量超过Java堆中最大的容量时,就会抛出OutOfMemoryError异常。在Java虚拟机中可以用-Xms参数和-Xmx参数设置Java堆的容量大小。

-Xms // 设置堆的最小值

-Xmx // 设置堆的最大值

当-Xms和-Xmx参数不一致时,如果存储的对象超过-Xms时,Java堆将进行自动扩展。下面我们将-Xms和-Xmx设置为一致,以避免Java堆的自动扩展,方便我们演示Java堆的溢出。

public class HeapOOM {    static class OOMObject {    }    public static void main(String[] args) {        List<OOMObject> list = new ArrayList<OOMObject>();        while (true) {            list.add(new OOMObject());        }    }}

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid295192.hprof ...
Heap dump file created [28138436 bytes in 0.162 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3210)
    at java.util.Arrays.copyOf(Arrays.java:3181)
    at java.util.ArrayList.grow(ArrayList.java:261)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
    at java.util.ArrayList.add(ArrayList.java:458)
    at HeapOOM.main(HeapOOM.java:17)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
OutOfMemoryError异常
  • 栈溢出

栈的容量由参数-Xss设置。在Java栈中有两种情况可能会抛出异常

  1. 如果线程请求栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
  2. 如果虚拟机在扩展栈时无法获得足够的内存空间时,则抛出OutOfMemoryError异常。

下面我们通过两个例子来演示上述的异常情况。

public class StackOOM {    private int stackLength = 1;    public void stackLeak() {        stackLength++;        stackLeak();    }    public static void main(String[] args) {        StackOOM stackOOM = new StackOOM();        stackOOM.stackLeak();    }}

Exception in thread "main" java.lang.StackOverflowError
    at StackOOM.stackLeak(StackOOM.java:11)
    at StackOOM.stackLeak(StackOOM.java:12)
    at StackOOM.stackLeak(StackOOM.java:12)
    at StackOOM.stackLeak(StackOOM.java:12)
    at StackOOM.stackLeak(StackOOM.java:12)
    at StackOOM.stackLeak(StackOOM.java:12)
    at StackOOM.stackLeak(StackOOM.java:12)

由此可见,在单线程的情况下,无论栈的大小是多少,当内存无法分配时,虚拟机都会抛出StackOverflowError异常。那如何才能抛出OutOfMemoryError异常呢?我们先看下面的例子,然后我们在做详细解释。

public class StackOOM {    public void dontStop() {        while (true) {        }    }    public void stackLeakByThread() {        while (true) {            Thread thread = new Thread(new Runnable() {                @Override                public void run() {                    dontStop();                }            });            thread.start();        }    }    public static void main(String[] args) {        StackOOM stackOOM = new StackOOM();        stackOOM.stackLeakByThread();    }}

上述的代码不但会抛出OutOfMemoryError异常,还会导致系统卡死,所以要慎重执行上面代码。那为什么重复创建线程就会导致虚拟机栈内存的溢出呢?这是因为在其它文章中我们已经介绍过了,线程都有自己的独立内存空间,并且每个线程的内存空间大小是有限制的,如果创建的线程空间大小超过了系统内存时,如果继续创建线程,虚拟机无法为栈分配空间了,所以就会抛出OutOfMemoryError异常。

  • 方法区和运行时常量池

我们可以用下面的参数来设置方法区的大小

-XX:PermSize // 方法区设置的最小值
-XX:MaxPermSize // 方法区设置的最大值
  • 本机直接内存溢出

我们可以用下面参数指定本机容量,如果不设置默认与堆的最大值一样。

-XX:MaxDirectMemorySize

原创文章,作者:二十四分之七倍根号六,如若转载,请注明出处:https://jilinwula.com/167.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注

联系我们

邮件:admin@jilinwula.com

QQ:923505769

 

QR code