【Java】内存溢出异常

内存溢出异常即OutOfMemoryError, 在Java虚拟机规范中规定, 除程序计数器以外, 其他内存区域都有可能出现OutOfMemoryError

下面就针对不同区域出现的异常进行分析.

虚拟机参数设置

为了能够简单模拟内存溢出异常, 需要给JVM添加堆栈大小, 打印GC堆等参数.下面是一些常用参数

设置

  • -Xms 设置java程序启动时初始堆大小
  • -Xmx 设置java程序能获得的最大堆大小
  • -Xss 设置每个线程的堆栈大小,JDK5.0以后每个线程堆栈默认大小为1M,以前每个线程堆栈默认大小为256K.
  • -Xmn 设置年轻代大小( eden + 2 survivor space )
  • -XX:SurvivorRatio=n Eden区与Survivor区的大小比值, 例如值为8表示Eden : Survivor from : Survivor to = 8 : 1 : 1
  • -XX:NewRatio=n 设置年老代和年轻代的比值, 例如值为3则表示年老代和年轻代的比值为 3 : 1

打印信息

  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintHeapAtGC 打印GC时堆的信息
  • -XX:+HeapDumpOnOutOfMemoryError 内存溢出异常时dump出当前内存堆快照

Java堆溢出

堆用来存放对象实例, 是GC的主要区域, 如果堆中的对象不能被有效释放, 并且不断地创建对象, 就会导致OOM异常.


/**
 * VM Args: -Xms20m -Xmx20m -xx:+HeapDumpOnOutOfMemoryError
 * @author: shuo
 * @date: 2019/08/05
 */
public class HeapOOM {

    static class OOMObject{}

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();
        while (true)
        {
            list.add(new OOMObject());
        }
    }
}
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid11300.hprof ...
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:265)
	at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
	at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
	at java.util.ArrayList.add(ArrayList.java:462)
	at JavaTest.jvm.HeapOOM.main(HeapOOM.java:19)
Heap dump file created [28340442 bytes in 0.071 secs]

利用内存映像分析工具分析dump下来的快照可以分析内存溢出的原因.

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

Java虚拟机规范规定了栈会出现两种溢出异常

  • StackOverFlowError 堆溢出, 线程中栈内存不够用时抛出
/**
 * VM Args: -Xss128k
 * @author: shuo
 * @date: 2019/08/05
 */
public class StackOF {
    private int stackLength = 1;

    public void stackLeak()
    {
        stackLength++;
        stackLeak();
    }
    public static void main(String[] args) {
        StackOF stackOf = new StackOF();
        try {
            stackOf.stackLeak();
        }catch (Throwable e)
        {
            System.out.println(stackOf.stackLength);
            e.printStackTrace();
        }
    }
}

28260
java.lang.StackOverflowError
	at JavaTest.jvm.StackOF.stackLeak(StackOF.java:16)
	at JavaTest.jvm.StackOF.stackLeak(StackOF.java:16)
	at JavaTest.jvm.StackOF.stackLeak(StackOF.java:16)
	..........
  • OutOfMemoryError 内存溢出, 线程要申请栈空间但内存不够时抛出
/**
 * -Xss2M
 * @author: shuo
 * @date: 2019/08/05
 */
public class StackOOM {
    private void dontStop()
    {
        while (true)
        {

        }
    }

    public void  stackLeak()
    {
        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.stackLeak();
    }
}

运行时常量池溢出

/**
 * VM Args:-XX:PermSize=1M -XX:MaxPermSize=1M
 * @author: shuo
 * @date: 2019/08/05
 */
public class RuntimeConstantPoolOOM {
    public static void main(String[] args) {
        List<String> list = new ArrayList();
        for (int i = 0;; i++) {
            list.add(String.valueOf(i).intern());
        }
    }
}

在JDK1.7以前

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space  

PermGen space 说明运行时常量池在永久代里

在JDK1.7以后while会一直运行下去。
因为JDK1.7的运行时常量池在堆中
需要重新设置虚拟机参数

-Xmx20m -Xms20m -XX:-UseGCOverheadLimit

运行后报错

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

Java heap space 运行时常量池在堆里

jdk1.6常量池放在方法区,jdk1.7常量池放在堆内存,jdk1.8放在元空间里面,和堆相独立。所以string的intern方法在不同版本会有不同表现。

方法区溢出

方法区用于存放Class的相关信息, 例如类名, 访问修饰符, 常量池, 字段描述符, 方法描述符等。
可以产生大量的类填满方法区,直到溢出。
利用CGLib直接操作字节码产生大量的动态类。

/**
 * VM Args: -XX:PerSize=10M -XX:MaxPermSize=10M
 * @author: shuo
 * @date: 2019/08/05
 */
public class JavaMethodAreaOOM {
    public static void main(String[] args) {
        while (true)
        {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(objects, args);
                }
            });
            enhancer.create();
        }
    }

    static class OOMObject{

    }
}
Caused by: java.lang.OutOfMemoryError: PermGen space

参考: 《深入理解Java虚拟机》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值