java虚拟机规范-运行时栈帧

时间:2024-01-02 22:40:26

前言

java虚拟机是java跨平台的基石,本文的描述以jdk7.0为准,其他版本可能会有一些微调。

引用

栈帧

  • 每当一个java方法被执行时都会在虚拟机中新创建一个栈帧,方法调用结束后即被销毁。
  • 栈帧存储空间为虚拟机栈,每一个栈帧都有自己的局部变量表、操作数栈和指向当前方法所属的类的运行时常量池的引用。
  • 在每个线程中,只有目前正在执行的那个方法的栈帧是活动的。这个栈帧就称为当前栈帧(current frame),这个栈帧对应的方法称之为当前方法(current method)。定义这个方法的类就称之为当前类(current class)。对局部变量表和操作数栈的各种操作,通常都指的是对当前栈帧的局部变量表和操作数栈进行的操作。如下图所示,
  • java虚拟机规范-运行时栈帧

变量

局部变量

  • 每一个栈帧包含一组局部变量,这组局部变量中包含了方法运行时所需要的所有的变量(含this引用)、方法参数和其他定义的局部变量。局部变量的类型包括以下几种:
    • boolean
    • byte
    • char
    • short
    • int
    • float
    • reference
    • returnAddress
    • long
    • double
  • 除了long和double外,其他所有的变量在局部变量表中都只占一个槽位(slot),而long和double占2个槽位。
  • 特别值得一提的是,当一个实例方法被调用的时候,局部变量表中的第0个局部变量一定是用来存储被调用的实例方法所在的对象引用。(this关键字,可以参考下面示例中的字节码截图),后续的其他参数将会传递至从1开始的局部变量表位置上。
  • 示例:
    • 以下图中的代码为例:
    • java虚拟机规范-运行时栈帧
    • 对应的字节码如下:
    • java虚拟机规范-运行时栈帧
    • 具体的操作流程示意:
    • java虚拟机规范-运行时栈帧
    • java虚拟机规范-运行时栈帧
  • 释义:
    • 从上图中我们可以看出例如int x=12,在jvm中对应着两个指令
      • bipush 10,bipush指令用于将一个byte作为一个整型数字插入到操作数栈中(oprand stack),这里将10插入到操作数栈
      • istore_1,istore_1指令(实际上这个应该称之为操作码opcode,指令是指操作码加上操作数oprand)是istore_指令组中的一个,用于将一个整型数字存储到局部变量中,<n>代表的是局部变量表中的存储位置,同时只能为0,1,2,3;超过3了就只能使用istore指令。
    • 注意,istore_1操作码和istore 4指令两者的区别为
      • 前者只是一个操作码,只占一个单位的长度,后者是操作码和操作数组合的指令,占2个单位的长度。(注意看Code中每一行指令前面的数字)
  • 思考:
    • 看了上图的字节码中可能有人会问,为什么还需要istore和iload这两个指令呢?这是因为栈本身不是用来存储数据的,而是用局部变量表来存储。局部变量通过索引寻址。