Java内存区域之栈内存溢出

栈内存溢出

Java内存区域Java虚拟机栈章节介绍过,Java虚拟机栈会发生两种异常StackOverflowError、OutOfMemoryError。

  • StackOverflowError
    线程请求的栈深度大于虚拟机所允许的栈深度,将抛出StackOverflowError
  • OutOfMemoryError
    如果Java虚拟机的栈容量可以动态扩展(HotSpot无法进行扩展,因此不会因为扩展而导致OOM,但如果申请时就失败,依然会出现OOM),当扩展时无法申请到足够的内存会抛出OutOfMemoryError

本文将通过代码演示这两种异常,示例采用Win10下的JDK 8运行,不同的JDK版本可能会有不同的结果。版本信息如下:
在这里插入图片描述

StackOverflowError

HotSpot虚拟机中,Java虚拟机栈与本地方法栈合二为一。通过-Xss参数可以设置栈内存大小,默认值一般在1M左右。linux可通过以下命令查看默认值:

[root@faith ~]# java -XX:+PrintFlagsFinal -version | grep "intx ThreadStackSize"
     intx ThreadStackSize                           = 1024                                {pd product}
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)

上面显示的单位是kb,与-Xss设置时单位b不一致。

StackOverflowError示例:

package jvm.oom;

/**
 * 栈StackOverflowError测试
 * <p>
 * 使用递归不断增加栈帧,填满栈内存
 *
 * @author faith.huan 2020-01-09 22:47
 */
public class JavaStackSoe {

    /**
     * 栈帧计数
     */
    private int frameCount = 0;

    /**
     * 递归调用添加栈帧,并增加计数
     */
    private void addFrame() {
        frameCount++;
        addFrame();
    }

    public static void main(String[] args) {
        JavaStackSoe soe = new JavaStackSoe();
        try {
            soe.addFrame();
        } catch (StackOverflowError e) {
            System.out.println("StackOverflowError..frameCount==> " + soe.frameCount);
        }
    }

}

执行上面代码后输出:(每次执行都会有不同结果,但相差不会太大)

StackOverflowError..frameCount==> 30215

当发生StackOverflowError时,Java虚拟机栈状态如下,此时无法容纳更多的栈帧,所以抛出StackOverflowError
在这里插入图片描述
上面代码在允许动态扩展栈容量大小的虚拟机上,可能会产生不同结果。例如在Classic VM上会产生OOM,鉴于此虚拟机已停用20年就不讨论了。

OutOfMemoryError

理论上栈内存OOM可以通过不断的新建线程来模拟,因为每个线程都需要一定的栈内存,当内存接近物理机内存时,会产生OOM异常。
栈内存溢出示例代码:

package jvm.oom;

/**
 * 通过不断创建线程,让虚拟机出现栈内存异常
 *
 * @author faith.huan 2020-01-09 22:18
 */
public class JavaStackOom {

    public static void main(String[] args) throws InterruptedException {
        int threadNum = 1;

        while (true) {
            new Thread(() -> {
                try {
                    Thread.sleep(Integer.MAX_VALUE);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "t-" + (threadNum++)).start();
            Thread.sleep(1);
            System.out.println("threadNum = " + threadNum);
        }
    }

}

理论终归是理论,上面的代码在64位Win10跑了10分钟,创建了2万个线程,依然未出现OOM,只是线程超过8000时,线程创建速度下降,不同机器会有不同测试结果。

根据《深入理解java虚拟机》代码示例,上面加有注释,请在32位系统下运行,可能也印证了64位下很难出现OOM。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值