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

时间:2022-12-27 21:27:25

介绍

关于虚拟机栈和本地方法栈,再Java虚拟机规范中描述了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出*Error异常。
  • 如果虚拟机再扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

注意:在栈空间无法分配时,到底时内存太小,还是已经使用的栈空间太大,其本质是同一件事情

实验1(*Error)

使用两种方法测试均无法产生OutOfMemoryError异常,尝试的结果都是获得*Error异常。
- 使用-Xss参数减少栈内容内存容量。结果:抛出*Error异常,异常出现时输出的堆栈深度缩小。
- 定义了大量的本地变量,增大此方法帧中本地变量表的长度。结果:*Error异常时输出的堆栈深度相应减小。
代码:

/** * * VM args : -Xss128k * 使用: 虚拟机栈和本地方法栈OOM测试 * */
public class JavaVMStackSOF {

    private int stackLength = 1;

    public void stackLeak()
    {
        stackLength++;
        stackLeak();
    }

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

得到的结果:
Exception in thread “main” stack length:981
java.lang.*Error
at Chapter2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:14)
at Chapter2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:15)
at Chapter2.JavaVMStackSOF.stackLeak(JavaVMStackSOF.java:15)

实验结果表明:在单个线程下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机都抛出的时*Error异常。

实验2(OutOfMemoryError)

在看完上面的情况后,出现了一个问题。那就是如何创造一个内存溢出的异常。这里,我们采用了创建线程的方式来消耗内存。操作系统给每个进程的内存是有限制的。例如32位系统的windows限制为2GB。剩余的内存为2GB减去Xmx(最大堆容量),再减去MaxPermSize(最大方法区容量),程序计数器消耗内存很小,忽略。如果虚拟机进程本身消耗的内存不计算在内,剩下的内存就由虚拟机栈和本地方法栈瓜分。每个线程分配到的栈容量越大,可以建立的线程数量自然就越少,建立线程时就越容易把剩下的内存耗尽。

如果测试不限于单线程,通过不断创建线程的方式是可以得到内存溢出的异常。
代码如下:

package Chapter2;
/** * Vm args: -Xss2M(这时候不妨设大点儿) * test: 创建线程导致内存溢出异常 */
public class JavaVMStackOOM {

    private 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)
    {
        JavaVMStackOOM oom = new JavaVMStackOOM();
        oom.stackLeakByThread();
    }
}

注意:由于在windows平台的虚拟机中,Java的线程时映射到操作系统中的内核线程上的,因此上述代码执行有较大危险,可能会导致死机