1. 前言
测试环境64 位 win7 + JDK1.8
-Xss
限制虚拟机栈的大小
-Xoss
限制本地方法栈大小
由于HotSpot虚拟机不区分虚拟机栈和本地方法栈,对于HotSpot来说,-Xoss
参数虽然存在,但是没有实际效果,栈容量只能由-Xss
来设置。
在《虚拟机规范》中描述了两种异常:
- 如果在线程请求的栈深度大于虚拟机所允许的最大深度,将抛出
StackOverflowError
异常 - 如果虚拟机的栈内存允许动态扩展,当栈容量无法申请到足够的内存时,将抛出
OutOfMemoryError
异常
需要注意的是,虽然规范允许虚机设置是否支持扩展,但是Hotspot是选则不支持扩展的。并且由于不支持扩展,理论上就不存在运行期间
出现内存溢出的情况,除非创建线程时,首次申请内存时报内存溢出,也就是说Hotspot只会报栈溢出!
本文的目的就是验证Hotspot只会报栈溢出!
2. 虚拟机栈深度报错
思路:通过-Xss
限制单个栈内存的大小,这里我们限定为为128K,当然128K也是32位windows下 jdk6一般的默认大小。如果是64位windows系统下的jdk11,则会提示不能低于180K,而这个值在linux下则可能是228K,如果低于这些值,那么在启动的时候会报错。
代码清单2-4:
注意设置
-Xss128k
,限定了栈的上限,因此很快就报错
/**
* VM Args:-Xss128k
*
* @author zzm
*/
public class JavaVMStackSOF_1 {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF_1 oom = new JavaVMStackSOF_1();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
执行结果,与书上相差不大:
stack length:2401
Exception in thread "main" java.lang.StackOverflowError
at org.fenixsoft.jvm.chapter2.JavaVMStackSOF_1.stackLeak(JavaVMStackSOF_1.java:13)
栈的上限约是2401个,说明栈不能无限的深度,在递归或调用方法层次时,注意深度!
3. 栈帧申请内存不够报错
栈是由栈帧组成的,栈帧的个数就是栈的深度,可以自定百度栈帧的定义。栈帧又由局部变量表、操作数栈等构成,因此,如果增加局部变量表的长度(也就是局部变量的个数),那么单个栈帧的体积就变大,由于总容量是不可扩容的,因此,深度就会变少。
下面就来验证上述理论:
代码清单2-5:
定义了大量的本地变量,增大此方法帧中本地变量表的长度。本例中是100个long类型的变量,提前耗尽内存。
注意:没有限制栈的大小,不设置-Xss
/**
* VM: JDK 1.0.2, Sun Classic VM
*
* @author zzm
*/
public class JavaVMStackSOF_3 {
private static int stackLength = 0;
public static void test() {
long unused1, unused2, unused3, unused4, unused5, unused6, unused7, unused8, unused9, unused10, unused11, unused12, unused13, unused14, unused15, unused16, unused17, unused18, unused19, unused20, unused21, unused22, unused23, unused24, unused25, unused26, unused27, unused28, unused29, unused30, unused31, unused32, unused33, unused34, unused35, unused36, unused37, unused38, unused39, unused40, unused41, unused42, unused43, unused44, unused45, unused46, unused47, unused48, unused49, unused50, unused51, unused52, unused53, unused54, unused55, unused56, unused57, unused58, unused59, unused60, unused61, unused62, unused63, unused64, unused65, unused66, unused67, unused68, unused69, unused70, unused71, unused72, unused73, unused74, unused75, unused76, unused77, unused78, unused79, unused80, unused81, unused82, unused83, unused84, unused85, unused86, unused87, unused88, unused89, unused90, unused91, unused92, unused93, unused94, unused95, unused96, unused97, unused98, unused99, unused100;
stackLength++;
test();
unused1 = unused2 = unused3 = unused4 = unused5 = unused6 = unused7 = unused8 = unused9 = unused10 = unused11 = unused12 = unused13 = unused14 = unused15 = unused16 = unused17 = unused18 = unused19 = unused20 = unused21 = unused22 = unused23 = unused24 = unused25 = unused26 = unused27 = unused28 = unused29 = unused30 = unused31 = unused32 = unused33 = unused34 = unused35 = unused36 = unused37 = unused38 = unused39 = unused40 = unused41 = unused42 = unused43 = unused44 = unused45 = unused46 = unused47 = unused48 = unused49 = unused50 = unused51 = unused52 = unused53 = unused54 = unused55 = unused56 = unused57 = unused58 = unused59 = unused60 = unused61 = unused62 = unused63 = unused64 = unused65 = unused66 = unused67 = unused68 = unused69 = unused70 = unused71 = unused72 = unused73 = unused74 = unused75 = unused76 = unused77 = unused78 = unused79 = unused80 = unused81 = unused82 = unused83 = unused84 = unused85 = unused86 = unused87 = unused88 = unused89 = unused90 = unused91 = unused92 = unused93 = unused94 = unused95 = unused96 = unused97 = unused98 = unused99 = unused100 = 0;
}
public static void main(String[] args) {
try {
test();
} catch (Error e) {
System.out.println("stack length:" + stackLength);
throw e;
}
}
}
执行结果,栈深度仅为359就耗尽内存:
stack length:359
Exception in thread "main" java.lang.StackOverflowError
at org.fenixsoft.jvm.chapter2.JavaVMStackSOF_3.test(JavaVMStackSOF_3.java:33)
4. 总结
由上面2个例子,可以看到,一般情况下,hotspot只会报栈溢出