JVM字节码与局部变量表

文章目录

局部变量表

每个线程的帧栈是独立的,每个线程中的方法调用会产生栈帧,栈帧中保存着方法执行的信息,例如局部变量表。

局部变量表是一个数组,大小在编译时就确定了,方法运行期间是不会改变局部变量表的大小。

局部变量表是一个数组中保存的结构叫做:slot

slot中的变量类型有下面10种(8种基本类型、引用类型、返回地址):

  1. byte
  2. bool
  3. char
  4. short
  5. int
  6. flot
  7. double
  8. long
  9. reference(引用类型)
  10. returnAddress(返回值地址)

除了long和double会占用2个slot,其他类型占用1个slot,byte、short、char、bool类型会转换为int类型存储。

JVM会为局部变量表中的每一个slot都分配一个访问索引,通过索引访问到局部变量表中指定的局部变量值。

索引从0开始,如果当前帧是由构造方法或者实例方法创建,那么该对象引用this会被存储在索引为0的slot。

当一个实例方法被调用的时候,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上。

局slot可以重用,如果一个局部变量过了其作用域,那么在其作用域之后申明的新的局部变量就可以复用过期局部变量的slot。

局部变量表不存在系统初始化的过程,所以定义局部变量必须手动初始化,这个编译时就能检查。

只要被局部变量表中直接或间接引用的对象都不会被回收。

javap

javap主要用来做反编译,可以查看编译之后的字节码,可以看javac处理之后的代码是什么样。

通过字节码可以看出:做了哪些优化,处理了哪些语法糖。

最常见3个参数:

  1. -c:查看反编译方法
  2. -l(小写L):-c的基础上多了局部变量表、指令索引和源码行号的对应关系
  3. -v: 所有信息
参数 作用说明
-version 版本信息
-v 或 -verbose 输出附加信息
-l 输出行号和局部变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类和成员
-p 或 -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出简洁版,只包含方法签名等基本信息
-sysinfo 显示正在处理的类的系统信息
-constants 显示最终常量
-classpath path 指定查找用户类文件的位置
-cp path 指定查找用户类文件的位置
-bootclasspath path 覆盖引导类文件的位置

字节码

字节码执行有一个操作栈,后面说的放入栈顶就是指操作栈顶。

指令分类

  1. 加载指令(Load):用于把变量从主存加载到栈内存
  2. 存储指令(Store):用于把变量从栈内存存储到主存
  3. 栈操作指令(Stack):用于对栈进行操作,比如交换栈顶元素,复制栈顶元素等
  4. 算术指令(Arithmetic):用于进行算术运算,比如加法,减法,乘法,除法等
  5. 转换指令(Conversion):用于进行类型转换,比如将int类型转为float类型等
  6. 比较指令(Comparison):用于进行数值比较
  7. 控制指令(Control):用于进行控制流转移
  8. 引用指令(Reference):用于操作对象,比如创建对象,获取对象的字段等
  9. 扩展指令(Extended):用于对指令集进行扩展
  10. 同步指令(Synchronization):用于线程同步

指令

指令 说明
load 加载操作,通常表示从局部变量表或数组中加载一个值到操作数栈
store 存储操作,通常表示将一个值从操作数栈存储到局部变量表或数组中
add 加运算
sub 减运算
mul 乘运算
div 除运算
rem 取余运算
and 位与运算
or 位或运算
xor 异或运算
neg 取反操作
shl 左移操作
shr 有符号右移
ushr 无符号右移操作
cmpeq 等于
cmpne 不等于
cmplt 小于
cmpge 大于等于
cmpgt 大于
cmple 小于等于
const 用于表示将常量加载到操作数栈
length 表示获取数组的长度
goto 表示无条件跳转
if 表示条件跳转
return 表示从方法返回
invoke 表示调用方法

指令数据类型前缀

字节码指令通常以一个字符作为前缀,表示操作数的类型。

指令前缀 代表数据类型
i 表示操作数是int类型
l 表示操作数是long类型
f 表示操作数是float类型
d 表示操作数是double类型
a 表示操作数是对象引用类型
b 表示操作数是byte类型
c 表示操作数是char类型
s 表示操作数是short类型

例如:iload表示加载一个int类型的局部变量,fadd表示将两个float类型的值相加。

加载和存储指令

xload_n:局部变量表加载到操作栈

xstore_n:操作栈数据存储到局部变量表

x:可以是i(int、byte、char、short、boolean类型)、l(long类型)、f(float类型)、d(double类型)、a(引用类型)

n:[0,3]

指令 描述
aload_0 将局部变量表中索引为0的slot的引用数据类型数据加载到操作栈顶
iload_1 将局部变量表中索引为1的slot的int类型数据加载到操作栈顶
lload_2 将局部变量表中索引为2的slot的long类型数据加载到操作栈顶
fload_3 将局部变量表中索引为3的slot的float类型数据加载到操作栈顶
astore_0 将操作栈顶引用类型数值存入局部变量表中第0个索引的slot中
istore_1 将操作栈顶int类型数据存入局部变量表中第1个索引的slot中
dstore_2 将操作栈顶double类型数据存入局部变量表中第2个索引的slot中
lstore_3 将操作栈顶long类型数据存入局部变量表中第3个索引的slot中

有善于思考的朋友可能就要问了:局部变量表大于4个怎么办呢?

使用:

  1. xload arg:例如 iload 4 表示将局部变量表中索引为4的slot放入栈顶
  2. xstore arg:例如 istore 4表示将栈顶元素放入局部变量表中索引为4的slot

x:可以是i(int、byte、char、short、boolean类型)、l(long类型)、f(float类型)、d(double类型)、a(引用类型)

加载常量

指令 含义
aconst_null 将null对象引用压入栈
iconst_m1 将int类型常量-1压入栈
iconst_0 将int类型常量0压入栈
iconst_1 将int类型常量1压入栈
iconst_2 将int类型常量2压入栈
iconst_3 将int类型常量3压入栈
iconst_4 将int类型常量4压入栈
iconst_5 将int类型常量5压入栈
lconst_0 将long类型常量0压入栈
lconst_1 将long类型常量1压入栈
fconst_0 将float类型常量0压入栈
fconst_1 将float类型常量1压入栈
dconst_0 将double类型常量0压入栈
dconst_1 将double类型常量1压入栈
bipush 将一个byte[-128,127]常量压入栈
sipush 将short[-32768,32767]常量压入栈
ldc int, float或String型常量值压入栈
ldc_w 将int, float或String型常量值压入栈
ldc2_w 将long、double常量值从常量池压入栈

如果int常量大于6个怎么整呢?

答案是:

  1. 小于127使用bipush x,例如:bipush 127
  2. 大于127小于等于32767使用sipush x,例如:sipush 32767
  3. 大于32767使用ldc x(常量引用),例如:ldc #31

算术指令

指令 描述
iadd 将栈顶两int类型数值相加并将结果压入栈顶
ladd 将栈顶两long类型数值相加并将结果压入栈顶
fadd 将栈顶两float类型数值相加并将结果压入栈顶
dadd 将栈顶两double类型数值相加并将结果压入栈顶
isub 将栈顶两int类型数值相减并将结果压入栈顶
lsub 将栈顶两long类型数值相减并将结果压入栈顶
fsub 将栈顶两float类型数值相减并将结果压入栈顶
dsub 将栈顶两double类型数值相减并将结果压入栈顶
imul 将栈顶两int类型数值相乘并将结果压入栈顶
lmul 将栈顶两long类型数值相乘并将结果压入栈顶
fmul 将栈顶两float类型数值相乘并将结果压入栈顶
dmul 将栈顶两double类型数值相乘并将结果压入栈顶
idiv 将栈顶两int类型数值相除并将结果压入栈顶
ldiv 将栈顶两long类型数值相除并将结果压入栈顶
fdiv 将栈顶两float类型数值相除并将结果压入栈顶
ddiv 将栈顶两double类型数值相除并将结果压入栈顶

其他指令

指令 描述
i2l 将栈顶int类型数值转换为long类型并压入栈顶
i2f 将栈顶int类型数值转换为float类型并压入栈顶
i2d 将栈顶int类型数值转换为double类型并压入栈顶
new 创建一个对象,并将引用值压入栈顶
anewarray 创建一个引用类型数组,并将引用值压入栈顶
arraylength 获取数组的长度值,并将长度值压入栈顶
pop 弹出栈顶数值
pop2 弹出栈顶的一个或两个数值
dup 复制栈顶数值并压入栈顶
ifeq 当栈顶int类型数值等于0时跳转
ifne 当栈顶int类型数值不等于0时跳转
goto 无条件跳转
invokevirtual 调用实例方法
invokespecial 调用构造函数,私有方法和父类方法
invokestatic 调用静态方法
return 从当前方法返回void
athrow 将栈顶的异常抛出
monitorenter 获取对象的锁
monitorexit 释放对象的锁
putfield 将栈顶的一个值存储到对象的字段中
getfield 从对象中取出一个字段的值

字节码示例说明

示例类:

java 复制代码
public class ByteCodeMain {

    public static final String HELLO = "HELLO";

    private Integer score;
    private String name;

    public ByteCodeMain(Integer score, String name) {
        this.score = score;
        this.name = name;
    }

    public int addScore(int add) {
        return this.score + doubleNum(add);
    }

    private static int doubleNum(int add){
        return add * 2;
    }

    public String sayHello(String append) {
        return HELLO + " " + this.name + append;
    }

    public Integer getScore() {
        return score;
    }

    public String getName() {
        return name;
    }

    public static void main(String[] args) {
        ByteCodeMain byteCodeMain = new ByteCodeMain(10, "Allen");
        System.out.println(byteCodeMain.getName());
        System.out.println(byteCodeMain.getScore());
        int resultNum = byteCodeMain.addScore(20);
        System.out.println(resultNum);
        String resultStr = byteCodeMain.sayHello(" 你好啊!");
        System.out.println(resultStr);
        System.out.println("常量:" + HELLO);
    }
}

编译获取class文件,然后使用javap反编译:

sh 复制代码
javac ByteCodeMain.java
javap -v ByteCodeMain.class

获取到如下的字节码

java 复制代码
Classfile ByteCodeMain.class
  Last modified ; size 2259 bytes
  SHA-256 checksum 7f96af8a9fec8835a0615c774629e0fcad2e6f00c7afaa33a88eb72cc834fd8f
  Compiled from "ByteCodeMain.java"
public class vip.meet.base.bytecode.ByteCodeMain
  minor version: 0
  major version: 61 // JDK17
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #8                          // vip/meet/base/bytecode/ByteCodeMain
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 3, methods: 7, attributes: 3
Constant pool: // 常量池
    #1 = Methodref          #2.#3         // java/lang/Object."<init>":()V
    #2 = Class              #4            // java/lang/Object
    #3 = NameAndType        #5:#6         // "<init>":()V
    #4 = Utf8               java/lang/Object
    #5 = Utf8               <init>
    #6 = Utf8               ()V
    #7 = Fieldref           #8.#9         // vip/meet/base/bytecode/ByteCodeMain.score:Ljava/lang/Integer;
    #8 = Class              #10           // vip/meet/base/bytecode/ByteCodeMain
    #9 = NameAndType        #11:#12       // score:Ljava/lang/Integer;
   #10 = Utf8               vip/meet/base/bytecode/ByteCodeMain
   #11 = Utf8               score
   #12 = Utf8               Ljava/lang/Integer;
   #13 = Fieldref           #8.#14        // vip/meet/base/bytecode/ByteCodeMain.name:Ljava/lang/String;
   #14 = NameAndType        #15:#16       // name:Ljava/lang/String;
   #15 = Utf8               name
   #16 = Utf8               Ljava/lang/String;
   #17 = Methodref          #18.#19       // java/lang/Integer.intValue:()I
   #18 = Class              #20           // java/lang/Integer
   #19 = NameAndType        #21:#22       // intValue:()I
   #20 = Utf8               java/lang/Integer
   #21 = Utf8               intValue
   #22 = Utf8               ()I
   #23 = Methodref          #8.#24        // vip/meet/base/bytecode/ByteCodeMain.doubleNum:(I)I
   #24 = NameAndType        #25:#26       // doubleNum:(I)I
   #25 = Utf8               doubleNum
   #26 = Utf8               (I)I
   #27 = InvokeDynamic      #0:#28        // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
   #28 = NameAndType        #29:#30       // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
   #29 = Utf8               makeConcatWithConstants
   #30 = Utf8               (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
   #31 = Methodref          #18.#32       // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #32 = NameAndType        #33:#34       // valueOf:(I)Ljava/lang/Integer;
   #33 = Utf8               valueOf
   #34 = Utf8               (I)Ljava/lang/Integer;
   #35 = String             #36           // Allen
   #36 = Utf8               Allen
   #37 = Methodref          #8.#38        // vip/meet/base/bytecode/ByteCodeMain."<init>":(Ljava/lang/Integer;Ljava/lang/String;)V
   #38 = NameAndType        #5:#39        // "<init>":(Ljava/lang/Integer;Ljava/lang/String;)V
   #39 = Utf8               (Ljava/lang/Integer;Ljava/lang/String;)V
   #40 = Fieldref           #41.#42       // java/lang/System.out:Ljava/io/PrintStream;
   #41 = Class              #43           // java/lang/System
   #42 = NameAndType        #44:#45       // out:Ljava/io/PrintStream;
   #43 = Utf8               java/lang/System
   #44 = Utf8               out
   #45 = Utf8               Ljava/io/PrintStream;
   #46 = Methodref          #8.#47        // vip/meet/base/bytecode/ByteCodeMain.getName:()Ljava/lang/String;
   #47 = NameAndType        #48:#49       // getName:()Ljava/lang/String;
   #48 = Utf8               getName
   #49 = Utf8               ()Ljava/lang/String;
   #50 = Methodref          #51.#52       // java/io/PrintStream.println:(Ljava/lang/String;)V
   #51 = Class              #53           // java/io/PrintStream
   #52 = NameAndType        #54:#55       // println:(Ljava/lang/String;)V
   #53 = Utf8               java/io/PrintStream
   #54 = Utf8               println
   #55 = Utf8               (Ljava/lang/String;)V
   #56 = Methodref          #8.#57        // vip/meet/base/bytecode/ByteCodeMain.getScore:()Ljava/lang/Integer;
   #57 = NameAndType        #58:#59       // getScore:()Ljava/lang/Integer;
   #58 = Utf8               getScore
   #59 = Utf8               ()Ljava/lang/Integer;
   #60 = Methodref          #51.#61       // java/io/PrintStream.println:(Ljava/lang/Object;)V
   #61 = NameAndType        #54:#62       // println:(Ljava/lang/Object;)V
   #62 = Utf8               (Ljava/lang/Object;)V
   #63 = Methodref          #8.#64        // vip/meet/base/bytecode/ByteCodeMain.addScore:(I)I
   #64 = NameAndType        #65:#26       // addScore:(I)I
   #65 = Utf8               addScore
   #66 = Methodref          #51.#67       // java/io/PrintStream.println:(I)V
   #67 = NameAndType        #54:#68       // println:(I)V
   #68 = Utf8               (I)V
   #69 = String             #70           //  你好啊!
   #70 = Utf8                你好啊!
   #71 = Methodref          #8.#72        // vip/meet/base/bytecode/ByteCodeMain.sayHello:(Ljava/lang/String;)Ljava/lang/String;
   #72 = NameAndType        #73:#74       // sayHello:(Ljava/lang/String;)Ljava/lang/String;
   #73 = Utf8               sayHello
   #74 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
   #75 = String             #76           // 常量:HELLO
   #76 = Utf8               常量:HELLO
   #77 = Utf8               HELLO
   #78 = Utf8               ConstantValue
   #79 = String             #77           // HELLO
   #80 = Utf8               Code
   #81 = Utf8               LineNumberTable
   #82 = Utf8               LocalVariableTable
   #83 = Utf8               this
   #84 = Utf8               Lvip/meet/base/bytecode/ByteCodeMain;
   #85 = Utf8               MethodParameters
   #86 = Utf8               add
   #87 = Utf8               I
   #88 = Utf8               append
   #89 = Utf8               main
   #90 = Utf8               ([Ljava/lang/String;)V
   #91 = Utf8               args
   #92 = Utf8               [Ljava/lang/String;
   #93 = Utf8               byteCodeMain
   #94 = Utf8               resultNum
   #95 = Utf8               resultStr
   #96 = Utf8               SourceFile
   #97 = Utf8               ByteCodeMain.java
   #98 = Utf8               BootstrapMethods
   #99 = String             #100          // HELLO \u0001\u0001
  #100 = Utf8               HELLO \u0001\u0001
  #101 = MethodHandle       6:#102        // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #102 = Methodref          #103.#104     // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #103 = Class              #105          // java/lang/invoke/StringConcatFactory
  #104 = NameAndType        #29:#106      // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #105 = Utf8               java/lang/invoke/StringConcatFactory
  #106 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #107 = Utf8               InnerClasses
  #108 = Class              #109          // java/lang/invoke/MethodHandles$Lookup
  #109 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #110 = Class              #111          // java/lang/invoke/MethodHandles
  #111 = Utf8               java/lang/invoke/MethodHandles
  #112 = Utf8               Lookup
{
  public static final java.lang.String HELLO;
    descriptor: Ljava/lang/String;
    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: String HELLO

  public vip.meet.base.bytecode.ByteCodeMain(java.lang.Integer, java.lang.String);
    descriptor: (Ljava/lang/Integer;Ljava/lang/String;)V // 构造函数
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this
         1: invokespecial #1                  // 调用Object的构造函数,Method java/lang/Object."<init>":()V
         4: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this
         5: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,Integer(score)
         6: putfield      #7                  // 将栈顶元素score设置到对象变量,Field score:Ljava/lang/Integer;
         9: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this
        10: aload_2 // 加载局部变量表索引为2的slot数据到栈顶,String(name)
        11: putfield      #13                 // 将栈顶元素name设置到对象变量,Field name:Ljava/lang/String;
        14: return
      LineNumberTable:
        line 13: 0
        line 14: 4
        line 15: 9
        line 16: 14
      LocalVariableTable: // 局部变量表
        Start  Length  Slot  Name   Signature
            0      15     0  this   Lvip/meet/base/bytecode/ByteCodeMain;
            0      15     1 score   Ljava/lang/Integer;
            0      15     2  name   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      score
      name

  public int addScore(int);
    descriptor: (I)I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this
         1: getfield      #7                  // 获取对象变量放入栈顶,Field score:Ljava/lang/Integer;
         4: invokevirtual #17                 // 调用对象方法(将int转为Integer对象),Method java/lang/Integer.intValue:()I
         7: iload_1 // 加载局部变量表索引为1的slot数据到栈顶,add
         8: invokestatic  #23                 // 调用静态方法,Method doubleNum:(I)I
        11: iadd // 将栈顶2元素相加之和放入栈顶
        12: ireturn // 返回int类型
      LineNumberTable:
        line 19: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  this   Lvip/meet/base/bytecode/ByteCodeMain;
            0      13     1   add   I
    MethodParameters:
      Name                           Flags
      add

  public java.lang.String sayHello(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this
         1: getfield      #13                 // 获取对象变量放入栈顶,Field name:Ljava/lang/String;
         4: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,append
         5: invokedynamic #27,  0             // 调用动态方法,InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        10: areturn
      LineNumberTable:
        line 27: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lvip/meet/base/bytecode/ByteCodeMain;
            0      11     1 append   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      append

  public java.lang.Integer getScore();
    descriptor: ()Ljava/lang/Integer;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #7                  // Field score:Ljava/lang/Integer;
         4: areturn
      LineNumberTable:
        line 31: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lvip/meet/base/bytecode/ByteCodeMain;

  public java.lang.String getName();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #13                 // Field name:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 35: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lvip/meet/base/bytecode/ByteCodeMain;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=4, args_size=1
         0: new           #8                  // 创建ByteCodeMain对象,并放入栈顶,class vip/meet/base/bytecode/ByteCodeMain
         3: dup // 复制栈顶元素并压入栈顶
         4: bipush        10 // 将byte类型常量10压入栈顶,字面量常量10被处理成了一个字节的byte类型
         6: invokestatic  #31                 // 调用静态方法,Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
         9: ldc           #35                 // 将常量池#35常量放入栈顶,String Allen
        11: invokespecial #37                 // 调用构造方法,Method "<init>":(Ljava/lang/Integer;Ljava/lang/String;)V
        14: astore_1 // 将栈顶元素(上一步构建的ByteCodeMain对象)放入局部变量表索引为1的slot
        15: getstatic     #40                 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;
        18: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,ByteCodeMain对象
        19: invokevirtual #46                 // 调用实例方法,Method getName:()Ljava/lang/String;
        22: invokevirtual #50                 // 调用实例方法,Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: getstatic     #40                 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;
        28: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,ByteCodeMain对象
        29: invokevirtual #56                 // 调用实例方法,Method getScore:()Ljava/lang/Integer;
        32: invokevirtual #60                 // 调用实例方法,Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        35: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,ByteCodeMain对象
        36: bipush        20 // 将byte类型常量20压入栈顶,字面量常量20被处理成了一个字节的byte类型
        38: invokevirtual #63                 // 调用实例方法,Method addScore:(I)I
        41: istore_2 // 将栈顶元素(上一步addScore计算获得的值)放入局部变量表索引为2的slot
        42: getstatic     #40                 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;
        45: iload_2 // 加载局部变量表索引为2的slot数据到栈顶,resultNum
        46: invokevirtual #66                 // 调用实例方法,Method java/io/PrintStream.println:(I)V
        49: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,ByteCodeMain对象
        50: ldc           #69                 // 将常量池#69常量放入栈顶,String  你好啊!
        52: invokevirtual #71                 // 调用实例方法,Method sayHello:(Ljava/lang/String;)Ljava/lang/String;
        55: astore_3 // 将栈顶元素(sayHello方法返回值)放入局部变量表索引为3的slot,resultStr
        56: getstatic     #40                 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;
        59: aload_3 // 加载局部变量表索引为3的slot数据到栈顶,resultStr
        60: invokevirtual #50                 // 调用实例方法,Method java/io/PrintStream.println:(Ljava/lang/String;)V
        63: getstatic     #40                 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;
        66: ldc           #75                 // 将常量池#75常量放入栈顶,String 常量:HELLO
        68: invokevirtual #50                 // 调用实例方法,Method java/io/PrintStream.println:(Ljava/lang/String;)V
        71: return
      LineNumberTable:
        line 39: 0
        line 40: 15
        line 41: 25
        line 42: 35
        line 43: 42
        line 44: 49
        line 45: 56
        line 46: 63
        line 47: 71
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      72     0  args   [Ljava/lang/String;
           15      57     1 byteCodeMain   Lvip/meet/base/bytecode/ByteCodeMain;
           42      30     2 resultNum   I
           56      16     3 resultStr   Ljava/lang/String;
    MethodParameters:
      Name                           Flags
      args
}
SourceFile: "ByteCodeMain.java"
BootstrapMethods:
  0: #101 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #99 HELLO \u0001\u0001
InnerClasses:
  public static final #112= #108 of #110; // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
相关推荐
良技漫谈25 天前
Rust命令行,实现自动反编译Android APK包工具
android·开发语言·后端·程序人生·rust·反编译
云草桑2 个月前
逆向工程 反编译 C# net core
前端·c#·反编译·逆向工程
搬砖大师兄.3 个月前
c#如何加密exe程序防止反编译附软件
c#·反编译
LabVIEW开发3 个月前
LabVIEW软件反编译
labview·反编译
极客先躯3 个月前
java的jar反编译软件有哪些?
java·jar·工具·反编译·逆向工程
一叶飘舟5 个月前
ASM插桩——动态添加字段并生成get set 方法
java·数据库·字节码·插桩
一叶飘舟5 个月前
ASM之FieldVisitor创建变量
字节码·插桩
Elaine猿5 个月前
python magic_number对照表(python1.5-python3.12)
python·反编译·pyc
hummhumm5 个月前
第七站:Java彩虹桥——跨平台开发的奇迹
java·开发语言·spring boot·spring cloud·java-ee·跨平台·字节码