字节码内部指令

97

java代码,以下代码在实际文件中,在9行到13行

public static void main(String[] args) {
      LocalVariablesTest test = new LocalVariablesTest();
      int num = 10;
      test.test1();
}

javap命令反编译字节码文件

public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #3                  // class test3/LocalVariablesTest
         3: dup
         4: invokespecial #4                  // Method "<init>":()V
         7: astore_1
         8: bipush        10
        10: istore_2
        11: aload_1
        12: invokevirtual #5                  // Method test1:()V
        15: return
      LineNumberTable:
        line 10: 0
        line 11: 8
        line 12: 11
        line 13: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  args   [Ljava/lang/String;
            8       8     1  test   Ltest3/LocalVariablesTest;
           11       5     2   num   I

jclasslib概述

方法名称为main方法

方法入参是一个一维字符串数组,L代表是一个引用对象,V代表返回值为void

([Ljava/lang/String;)V 是JNI字段描述符

返回标识是public static 对应的16进制就是0x0009

字节码指令解析.png

Bytecode是字节码指令,和javap返回的指令一致

Exception Table 异常表

Misc 杂项

  • Maximum stack size: 最大操作数栈大小

  • Maximum local variables: 局部变量表最大长度

  • Code length: 字节码指令长度(对应字节码指令0到15 )

字节码指令解析2.png

行号对应表

java代码的行号与程序计数器行号的对应关系

字节码指令解析3.png

局部变量表

Start PC 对应程序计数器的行号

Length 对应程序计数器指令地址的作用域

可以同过行号对应表和源代码行号对照,Length就是每个变量在源码中的最大范围

Start PC + Line Number均等于Code Length

Index 下标

Name 参数名称

Descriptor 描述

字节码指令解析4.png

执行解析1

public class OperandStackTest {
    public void testAddOperation() {
        byte i = 15;
        int j = 8;
        int k = i + j;
    }
}

通过javap -v命令将反编译字节码文件

操作数栈最大长度2,局部变量表最大长度4

public void testAddOperation();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    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
  • bipush 15

byte类型会被识别为int,并在操作数栈中压入int类型的15

执行解析.svg

  • istore_1

将操作数栈的15弹出,并放入局部变量表索引1的位置

执行解析2.svg

  • bipush 8

在操作数栈中压入int类型的8

  • istore_2

将操作数栈的8弹出,并放入局部变量表索引2的位置

  • iload_1

获取局部变量表中索引1的位置,并压入操作数栈中

  • iload_2

获取局部变量表中索引2的位置,并压入操作数栈中

  • iadd

操作数栈中的8和15依次出栈,计算为23后压入操作数栈中

  • istore_3

将23出栈,并放入局部变量表中索引3的位置

  • return

方法结束,整个栈帧从虚拟机栈中弹出

执行解析2

public class MethodAreaDemo {
    public static void main(String[] args) {
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}

通过javap指令转换,关键内容截取

// 常量池部分
Constant pool:
   #1 = Methodref          #5.#24         // java/lang/Object."<init>":()V
   #2 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #27.#28        // java/io/PrintStream.println:(I)V
   #25 = Class             #31            // java/lang/System
   #26 = NameAndType       #32:#33        // out:Ljava/io/PrintStream;
   #31 = Utf8              java/lang/System
   #32 = Utf8              out
   #33 = Utf8              Ljava/io/PrintStream;
// 方法部分   
public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: sipush        500
         3: istore_1
         4: bipush        100
         6: istore_2
         7: iload_1
         8: iload_2
         9: idiv
        10: istore_3
        11: bipush        50
        13: istore        4
        15: getstatic     #2          // Field java/lang/System.out:Ljava/io/PrintStream;
        18: iload_3
        19: iload         4
        21: iadd
        22: invokevirtual #3          // Method java/io/PrintStream.println:(I)V
        25: return
  • sipush 500

将500压入操作数栈中

  • istore_1

将操作数栈中的500弹出,并放入局部变量表索引1的位置

  • bipush 100

将100压入操作数栈中

执行解析2_3-unvm.svg

  • istore_2

存入本地变量表中

执行解析2_4.svg

  • iload_1

将局部变量表下标1的位置取出,压入操作数栈中

执行解析2_5.svg

  • iload_2

将局部变量表下标2的位置取出,压入操作数栈中

执行解析2_6.svg

  • idiv

将操作数栈中的数据依次去除,后者除以前者,结果压入操作数栈中

  • istore_3

将操作数栈中的5弹出,存入本地变量表中

  • bipush 50

将50压入操作数栈中

  • istore 4

50弹出操作数栈,放入局部变量表中

  • getstatic #2

从常量池中取出#2,压入操作数栈中

  • iload_3

将操作数栈下标3的元素去除,压入操作数栈中

  • iload 4

将操作数栈下标4的元素压入操作数栈中

  • iadd

将操作数栈50和5弹出,相加后压入操作数栈

  • invokevirtual #3

弹出#2调用#3(println方法),控制台输出55

  • return

执行结束return,操作数栈弹出