• 【jvm】虚拟机栈之操作数栈


    一、说明
    • 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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    2.2 javap操作
    javap -v OperandStackTest.class
    
    • 1
    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."":()V
       #2 = Class              #23            // com/learning/stack/operand_stack/OperandStackTest
       #3 = Class              #24            // java/lang/Object
       #4 = Utf8               
       #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          // "":()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."":()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"
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    三、图示
    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压入操作数栈,以此类推
  • 相关阅读:
    [附源码]Python计算机毕业设计Django社区住户信息管理系统
    一个程序员离职,成本竟然这么恐怖!
    Spring Cloud Oauth2 扩展
    [题] 子矩阵的和 #二维前缀和
    微信小程序 java ssm电影迷爱好者交流平台
    pytest+requests实现自动化代码编写思路
    Redis 分片集群
    Java EE——阻塞队列
    如何应对量化策略的失效
    目标检测算法——3D公共数据集汇总(附下载链接)
  • 原文地址:https://blog.csdn.net/qq_32088869/article/details/133936814