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]常量压入栈
ldcint, 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从对象中取出一个字段的值

字节码示例说明

局部变量与字节码

示例类:

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反编译:

javac ByteCodeMain.java
javap -v ByteCodeMain.class

获取到如下的字节码

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值