介绍
关于虚拟机栈和本地方法栈,再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的线程时映射到操作系统中的内核线程上的,因此上述代码执行有较大危险,可能会导致死机