【15】Java字节码

Java方法栈帧的组成:操作数栈+局部变量表

操作数栈

  • Java字节码是Java虚拟机所使用的的指令集。它与JVM基于栈的计算模型是分不开的。

  • 在解释执行过程中,每当为 Java 方法分配栈桢时,Java 虚拟机往往需要开辟一块额外的空间作为操作数栈,来存放计算的操作数以及返回结果。

    • 执行每条执行之前,JVM要求该指令的操作数已被压入操作数栈中
    • 在执行指令时,JVM会将该指令所需要的操作数弹出,并将该指令的结果重新压入栈中

以iadd为例说明该执行过程

  • 正常情况下,操作数栈的压入弹出都是一条条指令完成的
    在抛出异常时,JVM会清空操作数栈上到所有内容,而后将异常实例的引用压入操作数栈

直接作用在操作数栈上的Java字节码指令

dup 复制栈顶元素

dup指令常用于复制new指令生成的未经初始化的引用

  • 执行new指令时,JVM将指向一块已分配的但未初始化的内存引用压入操作数栈

  • invokespecial指令将要以这个引用为调用者,调用其构造器
    该指令将消耗操作数栈上的元素,作为它的调用者和参数

  • 这之前利用dup指令复制一份new指令的结果,并用来调用构造器

    public void dup() {
    Object o = new Object();
    }

    // 对应的字节码
    public void dup();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
    stack=2, locals=2, args_size=1
    0: new // class java/lang/Object
    3: dup
    4: invokespecial // Method java/lang/Object."<init>":()V
    7: astore_1
    8: return

pop 舍弃栈顶元素

pop指令常用于舍弃调用指令的返回结果

  public static boolean bar() {
    return false;
  }

  public void foo() {
    bar();
  }
  // foo 方法对应的字节码如下:
  public void foo();
    0  invokestatic FooTest.bar() : boolean [24]
    3  pop
    4  return
invokestatic指令依然会将返回值压入pop方法的操作数栈
因此JVM需要执行额外的pop指令,将返回值舍弃
swap 交换栈顶两个元素的值

局部变量区

  • 字节码程序将计算的结果缓存在局部变量表

  • 局部变量表类似于一个数组,依次存放

    • this指针(针对实例方法)

    • 所传入的参数

    • 字节码中的局部变量

      public void locals(long l, float f) {
      {
      int i = 0;
      }
      {
      String s = "Hello Word";
      }
      }

      // 对应的字节码
      public void locals(long, float);
      descriptor: (JF)V
      flags: ACC_PUBLIC
      Code:
      stack=1, locals=5, args_size=3 //Java编译器在编译时就已经能确定操作数栈、局部变量表的大小以及参数个数(非static方法多一个this)
      0: iconst_0
      1: istore 4
      3: ldc // String Hello Word
      5: astore 4
      7: return

      1. locals是一个实例方法,局部变量表的第0个单元存放this指针
      2. 第一个参数为long类型,占用局部变量表的第1、2个单元
      3. 第二个参数为float类型,占用局部变量表的第3个单元
      4. 方法体内的两个代码块中,分别定义了局部变量i和s,两者的生命周期没有重合,Java编译器将它们编排至同一单元,即局部变量表的第4个单元(istore 4和astore 4)
  • 存储在局部变量区的值,通常需要加载至操作数栈中,方能进行计算,得到计算结果后再存储至局部变量数组中。这些加载、存储指令是区分类型的。例如,int 类型的加载指令为 iload,存储指令为 istore。

综合示例

public static int bar(int i) {
  return ((i + 1) - 2) * 3 / 4;
}
// 对应的字节码如下:
Code:
  stack=2, locals=1, args_size=1 //Java编译器在编译时就已经能确定操作数栈、局部变量表的大小为1以及参数个数1个(static方法无this)
     0: iload_0
     1: iconst_1
     2: iadd
     3: iconst_2
     4: isub
     5: iconst_3
     6: imul
     7: iconst_4
     8: idiv
     9: ireturn

可以看到上图中最多使用了两个操作数栈

相关推荐
IT技术分享社区19 分钟前
C#实战:使用腾讯云识别服务轻松提取火车票信息
开发语言·c#·云计算·腾讯云·共识算法
极客代码22 分钟前
【Python TensorFlow】入门到精通
开发语言·人工智能·python·深度学习·tensorflow
疯一样的码农29 分钟前
Python 正则表达式(RegEx)
开发语言·python·正则表达式
代码之光_198030 分钟前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi35 分钟前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
&岁月不待人&1 小时前
Kotlin by lazy和lateinit的使用及区别
android·开发语言·kotlin
StayInLove1 小时前
G1垃圾回收器日志详解
java·开发语言
对许1 小时前
SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“
java·log4j
无尽的大道1 小时前
Java字符串深度解析:String的实现、常量池与性能优化
java·开发语言·性能优化
爱吃生蚝的于勒1 小时前
深入学习指针(5)!!!!!!!!!!!!!!!
c语言·开发语言·数据结构·学习·计算机网络·算法