实战:OutOfMemoryError异常之 虚拟机栈和本地方法栈溢出

虚拟机参数

  • 虚拟机栈:-Xss128k,表示虚拟机栈最大内存为128k。
  • 本地方法栈:-Xoss128k,表示本地方法栈最大内存为128k。

        由于在HotSpot虚拟机中不区分虚拟机栈和本地方法栈,所以栈容量只由-Xss参数设定。

Java虚拟机规范中描述的两种异常

  • 如果线程请求栈深度大于虚拟机所允许最大深度(没有内存供继续分配),将抛出StackOverflowError异常。
  • 如果虚拟机在扩展栈时无法申请到足够内存,将抛出OutOfMemoryError异常。

单线程SOF测试

概述

        虚拟机栈由栈帧构成,每调用一次方法,将需要在虚拟机栈占用一块内存用于创建一个栈帧,当达到容量上限时,将不能创建栈帧,所以再次调用方法时会抛出异常。

代码清单

public class JavaVMStackSOF {

    private int stackLength = 1;

    public void stackLeak(){
        stackLength++;
        stackLeak(); // 迭代调用方法,创建栈帧,占用虚拟机栈的内存
    }

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

}

VM Options参数设置

 运行结果

循环创建线程导致OOM异常

概述 

        在单线程程序中,无法出现OOM异常;但是通过循环创建线程(线程体调用方法),的放肆可以产生OOM异常。此时OOM异常产生的原因与栈空间是否足够大无关。

        出现OOM的原因是,操作系统分配给每个虚拟机进程的内存是有限制的,比如32位Windows限制为2GB,除去所有线程共享的堆内存和方法区,每开辟一个线程,都需要为其分配一定的栈内存和程序计数器(比重很小)。所以当线程数目达到一定数量时,内存耗尽,此时再有新线程启动时,会抛出OOM异常,因为没有内存分配给它。至于栈空间的容量设置影响到的是开辟的线程数量,栈空间容量越大,能够开辟的线程个数就越少。

代码清单

public class JavaVMStackOOM {
    private void dontStop(){
        while (true){

        }
    }

    // 循环创建线程,直到内存耗尽,抛出OOM异常
    public void stackLeakByThread(){
        while (true){
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    dontStop();
                }
            });

            thread.start();
        }
    }

    public static void main(String[] args) {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}

VM Options参数设置

VM Options:-Xss2M // 设置大一些,每个线程需要的内存多,能够创建的线程数量少

运行结果

        运行结果已清晰提示:无法继续创建线程        

Exception in Thread "main" java.lang.OutOfMemoryError: unable to create new native thread

总结

  • 如果使用虚拟机默认参数,栈深度到达1000~2000完全没问题。大多数情况下,对于方法的正常调用,包括递归,这个深度都够用。
  • 如果是建立多线程导致的内存溢出,而且不能减少线程数或者更换为64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值