JVM之虚拟机栈

虚拟机栈的作用

虚拟机栈是线程JVM中线程私有的区域。

虚拟机栈是一个先进后出的队列,当一个线程开始执行一个方法,会为该方法创建一个栈帧,压入栈中,当该方法执行完毕,再将该栈帧出栈。每一个方法被调用直至执行完毕的过程, 就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

public class TestStack {
    public static void main(String[] args) {
        m1();
    }
    public static void m1() {
        m2();
    }
    public static void m2() {
    }
}

在这里插入图片描述

栈帧中有哪些东西

既然方法的执行对应入栈出栈的过程,每个方法对应一个栈帧,那么在栈帧中,就一定保存有方法执行所需的数据。

一般来说,一个方法对应的栈帧中,包含局部变量表,操作数栈,动态链接,返回地址。

局部变量表

局部变量表是一组局部变量值存储空间,用于存放方法参数和方法内部定义的局部变量。在Java文件编译为Class文件时,就在方法表的Code属性的max_locals数据项中确定了该方法需要分配的最大局部变量表的容量。

我们可以使用Jclasslib插件来验证一下。
在这里插入图片描述
从上面main方法对应的局部变量表可以看出,静态变量A不存在于局部变量表中。

下面我们再看一下 test() 方法,注意这是一个非静态方法。
在这里插入图片描述
我们可以看出,在test方法的局部变量表中,多了一个this局部变量。 所以我们可以得知,在非静态方法中,局部变量表的第一位默认是this变量,而在静态方法中,则没有这个变量。

操作数栈

操作数栈用来临时存储需要计算或执行的数据。

我们仍然可以用Jclasslib来看一下main方法对应的字节码指令。
在这里插入图片描述
下面我们详细看一下指令的内容:

0 iconst_0		//将int值0压入操作数栈
1 istore_1		//将操作数栈数据出栈保存到局部变量表的1号位(即将0保存到局部变量表的1号位)
2 iconst_1	    //将int值1压入操作数栈
3 istore_2		//将操作数栈数据出栈保存到局部变量表的2号位(即将1保存到局部变量表的2号位)
4 iload_1		 //从局部变量表1号位读取数据
5 iload_2		 //从局部变量表2号位读取数据
6 iadd			  //将加载的两个数据相加 
7 istore_3	    //将相加的结果保存到局部变量表的3号位
8 iconst_0	   //将int值0压入操作数栈
9 putstatic #2 <com/torry/TestStack.A> 	// 将值0赋予静态变量A
12 return		// 方法执行完成,返回(没有返回值,默认就是return)
动态链接

动态链接是栈帧中指向运行时常量池中方法的引用。

Class 文件中存放了大量的符号引用,字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分会在类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。

方法返回地址

方法返回地址是用来记录方法执行完毕后,使方法可以正常的返回到调用处继续执行。

当一个方法开始执行后,要使方法结束,只有两种方式:

第一种方式: 方法正常执行结束。执行引擎遇到任意一个方法返回的字节码指令(例如:areturn,return等),这时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)

第二种方式:在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理。无论是Java虚拟机内部产生的异常,还是代码中使用 athrow 字节码指令产生的异常,只要在本方法的异常处理器表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。

无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的程序计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常表来确定的,栈帧中一般不会保存这部分信息。

方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令后面的一条指令等。

StackOverflowError

在HotSpot虚拟机中,虚拟机栈的大小只能设置固定值, 不支持动态扩展容量。当栈的容量不足时(比如方法的无限递归调用导致撑爆栈的内存),会抛出StackOverflowError的异常。

虚拟机栈会出现OOM吗

一般情况下,虚拟机栈是不存在OOM(OutOfMemoryError)异常的。

不过有一些虚拟机,可能会支持动态扩展,那么如果在栈需要扩展时而又申请不到足够的内存,仍然会抛出OOM异常。这个异常的本质,仍然是内存不足。

HotSpot调整虚拟机栈大小的命令

在HotSpot虚拟机中,可以通过 -Xss容量大小 来设置虚拟机栈的容量。

比如:

-Xss2048k 或者 -Xss2048K //设置栈容量大小为2M

-Xss2m 或者 -Xss2M // 设置栈容量大小为2M

-Xss1G 或者 Xss1g // 设置栈容量大小为1G

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值