文章目录
- 局部变量表
- javap
- 字节码
- 指令分类
- 指令
- 指令数据类型前缀
- 加载和存储指令
- 加载常量
- 算术指令
- 其他指令
- 字节码示例说明
局部变量表
每个线程的帧栈是独立的,每个线程中的方法调用会产生栈帧,栈帧中保存着方法执行的信息,例如局部变量表。
局部变量表是一个数组,大小在编译时就确定了,方法运行期间是不会改变局部变量表的大小。
局部变量表是一个数组中保存的结构叫做:slot
slot中的变量类型有下面10种(8种基本类型、引用类型、返回地址):
- byte
- bool
- char
- short
- int
- flot
- double
- long
- reference(引用类型)
- 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个参数:
- -c:查看反编译方法
- -l(小写L):-c的基础上多了局部变量表、指令索引和源码行号的对应关系
- -v: 所有信息
参数 | 作用说明 |
---|---|
-version | 版本信息 |
-v 或 -verbose | 输出附加信息 |
-l | 输出行号和局部变量表 |
-public | 仅显示公共类和成员 |
-protected | 显示受保护的/公共类和成员 |
-package | 显示程序包/受保护的/公共类和成员 |
-p 或 -private | 显示所有类和成员 |
-c | 对代码进行反汇编 |
-s | 输出简洁版,只包含方法签名等基本信息 |
-sysinfo | 显示正在处理的类的系统信息 |
-constants | 显示最终常量 |
-classpath path | 指定查找用户类文件的位置 |
-cp path | 指定查找用户类文件的位置 |
-bootclasspath path | 覆盖引导类文件的位置 |
字节码
字节码执行有一个操作栈,后面说的放入栈顶就是指操作栈顶。
指令分类
- 加载指令(Load):用于把变量从主存加载到栈内存
- 存储指令(Store):用于把变量从栈内存存储到主存
- 栈操作指令(Stack):用于对栈进行操作,比如交换栈顶元素,复制栈顶元素等
- 算术指令(Arithmetic):用于进行算术运算,比如加法,减法,乘法,除法等
- 转换指令(Conversion):用于进行类型转换,比如将int类型转为float类型等
- 比较指令(Comparison):用于进行数值比较
- 控制指令(Control):用于进行控制流转移
- 引用指令(Reference):用于操作对象,比如创建对象,获取对象的字段等
- 扩展指令(Extended):用于对指令集进行扩展
- 同步指令(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个怎么办呢?
使用:
- xload arg:例如 iload 4 表示将局部变量表中索引为4的slot放入栈顶
- 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个怎么整呢?
答案是:
- 小于127使用bipush x,例如:bipush 127
- 大于127小于等于32767使用sipush x,例如:sipush 32767
- 大于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.classLast modified ; size 2259 bytesSHA-256 checksum 7f96af8a9fec8835a0615c774629e0fcad2e6f00c7afaa33a88eb72cc834fd8fCompiled from "ByteCodeMain.java"
public class vip.meet.base.bytecode.ByteCodeMainminor version: 0major version: 61 // JDK17flags: (0x0021) ACC_PUBLIC, ACC_SUPERthis_class: #8 // vip/meet/base/bytecode/ByteCodeMainsuper_class: #2 // java/lang/Objectinterfaces: 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_FINALConstantValue: String HELLOpublic vip.meet.base.bytecode.ByteCodeMain(java.lang.Integer, java.lang.String);descriptor: (Ljava/lang/Integer;Ljava/lang/String;)V // 构造函数flags: (0x0001) ACC_PUBLICCode:stack=2, locals=3, args_size=30: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this1: invokespecial #1 // 调用Object的构造函数,Method java/lang/Object."<init>":()V4: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this5: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,Integer(score)6: putfield #7 // 将栈顶元素score设置到对象变量,Field score:Ljava/lang/Integer;9: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this10: aload_2 // 加载局部变量表索引为2的slot数据到栈顶,String(name)11: putfield #13 // 将栈顶元素name设置到对象变量,Field name:Ljava/lang/String;14: returnLineNumberTable:line 13: 0line 14: 4line 15: 9line 16: 14LocalVariableTable: // 局部变量表Start Length Slot Name Signature0 15 0 this Lvip/meet/base/bytecode/ByteCodeMain;0 15 1 score Ljava/lang/Integer;0 15 2 name Ljava/lang/String;MethodParameters:Name Flagsscorenamepublic int addScore(int);descriptor: (I)Iflags: (0x0001) ACC_PUBLICCode:stack=2, locals=2, args_size=20: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this1: getfield #7 // 获取对象变量放入栈顶,Field score:Ljava/lang/Integer;4: invokevirtual #17 // 调用对象方法(将int转为Integer对象),Method java/lang/Integer.intValue:()I7: iload_1 // 加载局部变量表索引为1的slot数据到栈顶,add8: invokestatic #23 // 调用静态方法,Method doubleNum:(I)I11: iadd // 将栈顶2元素相加之和放入栈顶12: ireturn // 返回int类型LineNumberTable:line 19: 0LocalVariableTable:Start Length Slot Name Signature0 13 0 this Lvip/meet/base/bytecode/ByteCodeMain;0 13 1 add IMethodParameters:Name Flagsaddpublic java.lang.String sayHello(java.lang.String);descriptor: (Ljava/lang/String;)Ljava/lang/String;flags: (0x0001) ACC_PUBLICCode:stack=2, locals=2, args_size=20: aload_0 // 加载局部变量表索引为0的slot数据到栈顶,this1: getfield #13 // 获取对象变量放入栈顶,Field name:Ljava/lang/String;4: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,append5: invokedynamic #27, 0 // 调用动态方法,InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;10: areturnLineNumberTable:line 27: 0LocalVariableTable:Start Length Slot Name Signature0 11 0 this Lvip/meet/base/bytecode/ByteCodeMain;0 11 1 append Ljava/lang/String;MethodParameters:Name Flagsappendpublic java.lang.Integer getScore();descriptor: ()Ljava/lang/Integer;flags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: getfield #7 // Field score:Ljava/lang/Integer;4: areturnLineNumberTable:line 31: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lvip/meet/base/bytecode/ByteCodeMain;public java.lang.String getName();descriptor: ()Ljava/lang/String;flags: (0x0001) ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: getfield #13 // Field name:Ljava/lang/String;4: areturnLineNumberTable:line 35: 0LocalVariableTable:Start Length Slot Name Signature0 5 0 this Lvip/meet/base/bytecode/ByteCodeMain;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=4, locals=4, args_size=10: new #8 // 创建ByteCodeMain对象,并放入栈顶,class vip/meet/base/bytecode/ByteCodeMain3: 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 Allen11: invokespecial #37 // 调用构造方法,Method "<init>":(Ljava/lang/Integer;Ljava/lang/String;)V14: astore_1 // 将栈顶元素(上一步构建的ByteCodeMain对象)放入局部变量表索引为1的slot15: 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;)V25: 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;)V35: aload_1 // 加载局部变量表索引为1的slot数据到栈顶,ByteCodeMain对象36: bipush 20 // 将byte类型常量20压入栈顶,字面量常量20被处理成了一个字节的byte类型38: invokevirtual #63 // 调用实例方法,Method addScore:(I)I41: istore_2 // 将栈顶元素(上一步addScore计算获得的值)放入局部变量表索引为2的slot42: getstatic #40 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;45: iload_2 // 加载局部变量表索引为2的slot数据到栈顶,resultNum46: invokevirtual #66 // 调用实例方法,Method java/io/PrintStream.println:(I)V49: 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,resultStr56: getstatic #40 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;59: aload_3 // 加载局部变量表索引为3的slot数据到栈顶,resultStr60: invokevirtual #50 // 调用实例方法,Method java/io/PrintStream.println:(Ljava/lang/String;)V63: getstatic #40 // 获取静态变量(System.out),Field java/lang/System.out:Ljava/io/PrintStream;66: ldc #75 // 将常量池#75常量放入栈顶,String 常量:HELLO68: invokevirtual #50 // 调用实例方法,Method java/io/PrintStream.println:(Ljava/lang/String;)V71: returnLineNumberTable:line 39: 0line 40: 15line 41: 25line 42: 35line 43: 42line 44: 49line 45: 56line 46: 63line 47: 71LocalVariableTable:Start Length Slot Name Signature0 72 0 args [Ljava/lang/String;15 57 1 byteCodeMain Lvip/meet/base/bytecode/ByteCodeMain;42 30 2 resultNum I56 16 3 resultStr Ljava/lang/String;MethodParameters:Name Flagsargs
}
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