1.基本概念
????????操作数栈(Operand Stack)也常被称为操作栈,它是一个后入先出(LIFO)栈。同局部变量表一样. 操作数栈的最大深度也在编译的时候被写入到Code属性的max_ stacks数据项之中,操作数栈的深度都不会超过在max stacks数据项中设定的最大值。。操作数栈的每一个元素都可以是包括long和double在内的任意Java数据类型。32位数据类型所占的栈容量为1。64位数据类型所占的栈容量为2。操作数栈,主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间。
????????操作数栈并非采用访问索引的方式来进行数据访问的,而是通过标准的入栈(push)和出栈(pop)操作来完成一次数据访问。?当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写人和提取内容,也就是出栈和入栈操作。如果被调用的方法带有返回值的话,其返回值将会被压入当前栈帧的操作数栈中,并更新 PC 寄存器中下一条需要执行的字节码指令。
????????譬如在做算术运算的时候是通过将运算涉及的操作数栈压人栈顶后调用运算指令来进行的,又譬如在调用其他方法的时候是通过操作数栈来进行方法参数的传递。举个例子,例如整数加法的字节码指令iadd(i表示是int类型,注意boolean、char、short、boolean也会是i,add表示相加,具体需要参考字节码指令内容),这条指令在运行的时候要求操作数栈中最接近栈顶的两个元素已经存入了两个int 型的数值,当执行这个指令时,会把这两个int值出栈并相加,然后将相加的结果重新人栈。
????????操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,在编译程序代码的时候,编译器必须要严格保证这一 点,在类校验阶段的数据流分析中还要再次验证这一点。?再以上面的iadd指令为例,这个指令只能用于整型数的加法,它在执行时,最接近栈顶的两个元素的数据类型必须为int型,不能出现一个 long和一个float 使用iadd命令相加的情况。
????????另外在概念模型中,两个不同栈帧作为不同方法的虚拟机栈的元素,是完全相互独立的。但是在大多虚拟机的实现里都会进行一些优化处理, 令两个栈帧出现一部分重叠。 让下面栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠在一起,这样做不仅节约了一些空间,更重要的是在进行方法调用时就可以直接共用一部分数据,无须进行额外的参数复制传递了,重叠的过程如图所示。
????????Java虚拟机的解释执行引擎技称为“基于栈的执行引擎” ,里面的“栈” 就是操作数栈。
?测试:
public static void main(String[] args) {
TestOperandStack testOperandStack = new TestOperandStack();
//1.进入操作数栈
testOperandStack.add(1);
}
public int add(int a) {
// 1. 存在于局部变量表中
int rt = a + 1;
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
return rt;
}
使用JHSDB 工具查看栈的信息,结果如下:
?
|