目录
- 一、说明
- 二、图解
- 2.1 代码示例
- 2.2 javap操作
- 三、图示
- 3.1 bipush 15
- 3.2 istore_1
- 3.3 bipush 8
- 3.4 istore_2
- 3.5 iload_1
- 3.6 iload_2
- 3.7 iadd
- 3.8 istore_3
- 3.9 return结束
- 四、附加
一、说明
- 1.Operand Stack
- 2.栈可以使用数组或链表来实现
- 3.每一个独立的栈帧包含一个后进先出(Last-In-First-Out)的操作数栈
- 4.也可称为表达式栈(expression stack)
- 5.操作数栈在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈(push)/出栈(pop)
- 6.某些字节码指令将值压入操作数栈,其余的字节码指令将操作数取出栈,使用它们后再把结果压入栈
- 7.可以用来执行复制、交换、求和等操作
- 8.操作数栈主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间
- 9.操作数栈是jvm执行引擎的一个工作区,当一个方法刚开始执行的时候,一个新的栈帧也会被创建出来,这个方法的操作数栈是空的
- 10.每一个操作数栈都拥有一个明确的栈深度用于存储数值,所需的最大深度在编译器定义好了,保存在方法的code属性,为max_stack的值
- 11.栈中的任何一个元素都是可以任意的java数据类型,32bit的类型占用一个栈单位深度,64bit的类型占用两个栈单位深度
- 12.操作数栈并非采用访问索引的方式来进行数据访问,只能通过标准的入栈(push)和出栈(pop)操作来完成一次数据访问
- 13.被调用的方法带有返回值,其返回值将会被压入当前栈帧的操作数栈中,并更新pc寄存器中下一条需要执行的字节码指令
- 14.操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,这由编译器在编译器期间进行验证,同时在类加载过程中的类检验阶段的数据流分析阶段要再次验证
- 15.java虚拟机的解释引擎是基于栈的执行引擎,该栈指的就是操作数栈
二、图解
2.1 代码示例
package com.learning.stack.operand_stack;
/**
* @Author wangyouhui
* @Description 操作数栈
**/
public class OperandStackTest {
public static void main(String[] args) {
byte i = 15;
int j = 8;
int k = i + j;
}
}
2.2 javap操作
javap -v OperandStackTest.class
Classfile /F:/jdk-learning/jvm/target/classes/com/learning/stack/operand_stack/OperandStackTest.class
Last modified 2023-10-22; size 529 bytes
MD5 checksum 82f8abcaba4fd78948113a564c4f83a1
Compiled from "OperandStackTest.java"
public class com.learning.stack.operand_stack.OperandStackTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#22 // java/lang/Object."<init>":()V
#2 = Class #23 // com/learning/stack/operand_stack/OperandStackTest
#3 = Class #24 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lcom/learning/stack/operand_stack/OperandStackTest;
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 args
#14 = Utf8 [Ljava/lang/String;
#15 = Utf8 i
#16 = Utf8 B
#17 = Utf8 j
#18 = Utf8 I
#19 = Utf8 k
#20 = Utf8 SourceFile
#21 = Utf8 OperandStackTest.java
#22 = NameAndType #4:#5 // "<init>":()V
#23 = Utf8 com/learning/stack/operand_stack/OperandStackTest
#24 = Utf8 java/lang/Object
{
public com.learning.stack.operand_stack.OperandStackTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/learning/stack/operand_stack/OperandStackTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 15
2: istore_1
3: bipush 8
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: return
LineNumberTable:
line 9: 0
line 10: 3
line 11: 6
line 12: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 args [Ljava/lang/String;
3 8 1 i B
6 5 2 j I
10 1 3 k I
}
SourceFile: "OperandStackTest.java"
三、图示
3.1 bipush 15
- 1.操作数栈和局部变量表一开始是空的
- 2.pc寄存器记录要操作的指令地址是0
- 3.byte i = 15;
- 4.byte、short、char、boolean、int都以int型来保存,bipush中b表示byte,i表示int
- 5.bipush 15是将15push到操作数栈当中
3.2 istore_1
- 1.pc寄存器记录要操作的指令地址2
- 2.istore_1将栈顶的15存到局部变量表索引为1的位置
- 3.已经转化为int类型了,所以用istore
- 4.非静态方法,局部变量表索引为0的位置是this变量,因此istore存的是索引为1的位置
- 5.操作数栈的栈顶出栈后变为空的
3.3 bipush 8
- 1.pc寄存器记录要操作的指令地址3
- 2.把8压入操作数栈的栈顶
3.4 istore_2
- 1.pc寄存器记录要操作的指令地址5
- 2.将操作数栈的栈顶数据出栈,保存到局部变量表索引为2的位置
- 3.操作数栈的栈顶数据出栈后,变为空
3.5 iload_1
- 1.pc寄存器记录要操作的指令地址6
- 2.iload_1将局部变量表中索引为1位置的数据取出压入操作数栈的栈顶
3.6 iload_2
- 1.pc寄存器记录要操作的指令地址7
- 2.iload_2将局部变量表中索引为2位置的数据取出压入操作数栈的栈顶
3.7 iadd
- 1.pc寄存器记录要操作的指令地址8
- 2.iadd会弹出操作数栈栈顶的数据,执行引擎将字节码指令翻译为机器指令,从而让cpu计算俩数据的和23
- 3.iadd将cpu计算的结果23压入操作数栈的栈顶
3.8 istore_3
- 1.pc寄存器记录要操作的指令地址9
- 2.istore_3将操作数栈的栈顶数据出栈,保存到局部变量表索引为3的位置
- 3.操作数栈的栈顶数据出栈后,变为空
3.9 return结束
四、附加
- 1.操作数栈从一开始到结束,只用到2个空间,即最大深度为2,即stack=2
- 2.局部变量表最大索引位置为3,局部变量表最大长度为4,索引为0的位置是this变量,即locals=4
- 3.尽量会以最小的范围来匹配push,例如byte范围为-128到127,在这个范围内的数会用bipush压入操作数栈,short范围为-32768到32727,当一个整数超过了byte范围,但还在short范围内,会用sipush压入操作数栈,以此类推