Java虚拟机栈的出栈入栈流程及栈内存大小设置
前言
Java虚拟机栈(Java Virtual Machine Stacks)是线程私有的,即生命周期和线程相同。
Java虚拟机栈和线程同时创建,用于存储栈帧。每个方法在执行时都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
每一个方法从调用直到执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。虚拟机栈结构示意图如下:
我们来了解下出栈入栈的过程及虚拟机栈的参数设置。
出栈入栈过程
我们来通过一个实例来看下jvm中虚拟机栈的调用情况。
代码示例
先看下面的代码:
public class StackDemo {
public static void main(String[] args) {
System.out.println("main 方法start");
StackDemo stackDemo = new StackDemo();
stackDemo.methodA();
System.out.println("main 方法end");
}
public void methodA(){
System.out.println("methodA start");
methodB();
System.out.println("methodA end");
}
public void methodB(){
System.out.println("methodB start");
methodC();
System.out.println("methodB end");
}
public void methodC(){
System.out.println("methodC start");
System.out.println("methodC end");
}
}
说明:上述代码中main()调用methodA(),methodA()调用methodB(),methodB()调用methodC()。
具体步骤
我们在idea中通过debug模式来查看它的栈帧情况:
第一步,先执行main方法,main方法入栈:
第二步,执行到第15行,进入到methodA()方法,methodA()方法入栈:
第三步,执行到第22行,进入到methodB()方法,methodB()方法入栈:
第四步,执行到第28行,进入methodC()方法,methodC()方法入栈:
到目前为止,栈中的栈帧情况如下:
栈底部是main方法,上面依次是methodA,methodB,methodC。
第五步,然后继续往下执行,methodC执行完毕,栈情况如下:
可以看到methodC方法执行完毕后,methodC出栈,回到methodB方法中的下一行继续执行。
第六步,methodB执行完毕,methodB出栈,如下:
此时栈中只剩下methodA和main方法。
第七步,methodA执行完毕,methodA出栈:
此时只剩下main方法在栈中。
第八步,main方法执行完毕,栈中数据全部出栈:
到此,整个main方法执行结束。
设置虚拟机栈的大小
参数设置
-Xss 为jvm启动的每个线程分配的内存大小,默认JDK1.4中是256K,JDK1.5+中是1M
-Xss1m
-Xss1024k
-Xss1048576
代码实例
我们通过一个代码案例来看动态设置虚拟机栈的大小:
public class StackDemo {
static long count=0;
public static void main(String[] args) {
count++;
System.out.println(count);
main(args);
}
}
这是一个无限递归的方法,不管虚拟机栈设置多大,它总会栈溢出的。我们通过一个count的值来记录不同虚拟机栈大小下的栈溢出的情况。
我本地默认情况下的运行结果:
count达到8641后栈溢出。
然后我们在idea里面设置虚拟机栈的大小,设置为256k:
步骤如下:
由于256k比默认的1m要小很多,所以虚拟机栈应该会更快的溢出。
接下来我们进行验证:
可以看到,count到2192的时候就已经溢出了,符合我们的预期,说明参数设置有效。
总结
我们通过一个代码实例一步步的debug来观察了虚拟机栈的情况,从而对方法执行中的栈帧出栈入栈有了更清晰的认识。用-Xss参数对虚拟机栈的大小进行了设置,观察了设置后的效果。