JVM字节码与局部变量表
文章目录
- 局部变量表
- 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
相关文章:

JVM字节码与局部变量表
文章目录 局部变量表javap字节码指令分类 指令指令数据类型前缀加载和存储指令加载常量算术指令其他指令 字节码示例说明 局部变量表 每个线程的帧栈是独立的,每个线程中的方法调用会产生栈帧,栈帧中保存着方法执行的信息,例如局部变量表。 …...

Java许可政策再变,Oracle JDK 17 免费期将结束!
原文地址:https://www.infoworld.com/article/3478122/get-ready-for-more-java-licensing-changes.html Oracle JDK 17的许可协议将于9月变更回Oracle Technology Network License Agreement,这将迫使用户重新评估他们的使用策略。 有句老话说…...

网页交互模拟:模拟用户输入、点击、选择、滚动等交互操作
目录 一、理论基础 1.1 网页交互模拟的重要性 1.2 网页交互的基本原理 二、常用工具介绍 2.1 Selenium 2.2 Puppeteer 2.3 Cypress 2.4 TestCafe 三、实战案例 3.1 模拟用户输入 3.2 模拟用户点击 3.3 模拟用户选择 3.4 模拟滚动操作 四、最佳实践与优化 4.1 代…...

C sharp 学习 笔记
介绍 这篇文章是我学习C#语言的笔记 学的是哔哩哔哩刘铁锰老师2014年的课程 在学习C#之前已经学习过C语言了。看的是哔哩哔哩比特鹏哥的课程。他们讲的都很不错 正在更新, 大家可以在我的gitee仓库中下载笔记源文件、项目资料等 笔记源文件可以在Notion中导入…...

文章资讯职场话题网站源码整站资源自带2000+数据
介绍: 数据有点多,数据资源包比较大,压缩后还有250m左右。值钱的是数据,网站上传后直接可用,爽飞了 环境:NGINX1.18 mysql5.6 php7.2 代码下载...
c++ templates常用函数
说明 c templates学习中会遇到大量的模版常用函数,书上不会详细介绍,查看一个之后要永久记录一段时间之后再看看,这里总结一下。 undeclared(); undeclared();//若undeclared();未定义,则在第一阶段编译时报错 undeclared(t);…...

【重学 MySQL】三十一、字符串函数
【重学 MySQL】三十一、字符串函数 函数名称用法描述ASCII(S)返回字符串S中的第一个字符的ASCII码值CHAR_LENGTH(s)返回字符串s的字符数,与CHARACTER_LENGTH(s)相同LENGTH(s)返回字符串s的字节数,和字符集有关CONCAT(s1,s2,…,sn)连接s1,s2,…,sn为一个字…...

828华为云征文 | 使用Flexus云服务器X实例部署GLPI资产管理系统
828华为云征文 | 使用Flexus云服务器X实例部署GLPI资产管理系统 1. 部署环境说明2. 部署基础环境2.1. 操作系统基本配置2.2. 部署Nginx2.3. 部署MySQL2.4. 部署PHP 3. 部署GLPI资产管理系统 1. 部署环境说明 本次环境选择使用华为云Flexus云服务器X实例,因为其具有高…...
深入理解Go语言的面向对象编程、Git与GitHub的使用
Go语言以其简洁、高效和并发支持而广受欢迎。虽然Go不是一种传统的面向对象编程(OOP)语言,但它提供了一些特性,使我们能够模拟OOP的某些概念。在本文中,我们将深入探讨Go语言中的面向对象编程技巧,以及如何使用Git和GitHub进行版本控制。通过丰富的代码示例和详细的解释,…...

redis底层—通信协议RESP
...

JVM 调优篇6 可视化性能监控工具-JVisual VM
一 Visual VM 1.1 概述 Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具。 它集成了多个JDK命令行工具,使用Visual VM可用于显示虚拟机进程及进程的配置和环境信息(jps,jinfo),监视应用程序的CPU、GC、堆、方法区及线程的信息(jstat…...

C#学习笔记(三)Visual Studio安装与使用
博主刚开始接触C#,本系列为学习记录,如有错误欢迎各位大佬指正!期待互相交流! 上一篇文章中安装了Visual Studio Code来编写调试C#程序,但是博主的目标是编写带窗口的应用程序,了解之后发现需要安装Visual …...

element-plus的菜单组件el-menu
菜单是几乎是每个管理系统的软件系统中不可或缺的,element-plus提供的菜单组件可以快速完成大部分的菜单的需求开发, 该组件内置和vue-router的集成,使用起来很方便。 主要组件如下 el-menu 顶级菜单组件 主要属性 mode:决定菜单的展示模式…...
深入浅出通信原理
深入浅出通信原理 文章目录 深入浅出通信原理前言一、概述二、信号和频谱2.1 信号2.2 信号的分解与合成2.3 傅里叶变换的特性2.4 离散傅里叶变化 三 信道3.1 衰减和损耗3.2 多普勒效应 四 信源编码4.1 采样4.2 量化4.3 编码 五 基带信号的发送和接受5.1 脉冲成形5.2 眼图 六 频…...

Gitee Pipeline 从入门到实战【详细步骤】
文章目录 Gitee Pipeline 简介Gitee Pipeline 实战案例 1 - 前端部署输入源NPM 构建Docker 镜像构建Shell 命令执行案例 2 - 后端部署全局参数输入源Maven 构建Docker 镜像构建Shell 命令执行参考🚀 本文目标:快速了解 Gitee Pipeline,并实现前端及后端打包部署。 Gitee Pi…...

【贪心算法】贪心算法
贪心算法简介 1.什么是贪心算法2.贪心算法的特点3.学习贪心的方向 点赞👍👍收藏🌟🌟关注💖💖 你的支持是对我最大的鼓励,我们一起努力吧!😃😃 1.什么是贪心算法 与其说是…...

【网络原理】❤️Tcp 常用机制❤️ —— 延时应答,捎带应答, 面向字节流, 异常情况处理。保姆式详解 , 建议收藏 !!!
本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人…...

Scratch教学案例 —— 制作生日蛋糕
小虎鲸Scratch资源站-免费少儿编程Scratch作品源码,素材,教程分享网站! 简介 在这个教学案例中,我们将使用Scratch制作一个简单而有趣的生日蛋糕动画。通过这个项目,学生可以学习到如何使用Scratch中的基本编程块进行角色控制、造型切换、舞台背景设置以…...

【深度学习】搞懂卷积神经网络(一)
卷积神经网络是一种具有局部连接,权重共享等特性的深层前馈神经网络。一般是由卷积层,池化层,全连接层交叉堆叠而成,使用反向传播算法进行训练。卷积神经网络具有一定程度上的平移,缩放和旋转不变性,较前馈…...

VisionPro - 基础 - 00 模板匹配技术和在VP中的使用 - PMAlign - PatMax - (上)
前言 模板匹配是机器视觉领域,尤其是工业视觉领域内,自动化经常要使用的一个视觉算法应用模式。在VP里面,有几种简单的模版匹配的算子,这里大致介绍一下VP的PatMax。 在视觉应用领域,搜索匹配的特征是经常要用到的方…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

MySQL体系架构解析(三):MySQL目录与启动配置全解析
MySQL中的目录和文件 bin目录 在 MySQL 的安装目录下有一个特别重要的 bin 目录,这个目录下存放着许多可执行文件。与其他系统的可执行文件类似,这些可执行文件都是与服务器和客户端程序相关的。 启动MySQL服务器程序 在 UNIX 系统中,用…...
StarRocks 全面向量化执行引擎深度解析
StarRocks 全面向量化执行引擎深度解析 StarRocks 的向量化执行引擎是其高性能的核心设计,相比传统行式处理引擎(如MySQL),性能可提升 5-10倍。以下是分层拆解: 1. 向量化 vs 传统行式处理 维度行式处理向量化处理数…...
在Spring Boot中集成RabbitMQ的完整指南
前言 在现代微服务架构中,消息队列(Message Queue)是实现异步通信、解耦系统组件的重要工具。RabbitMQ 是一个流行的消息中间件,支持多种消息协议,具有高可靠性和可扩展性。 本博客将详细介绍如何在 Spring Boot 项目…...

Python异步编程:深入理解协程的原理与实践指南
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 持续学习,不断…...
生成对抗网络(GAN)损失函数解读
GAN损失函数的形式: 以下是对每个部分的解读: 1. , :这个部分表示生成器(Generator)G的目标是最小化损失函数。 :判别器(Discriminator)D的目标是最大化损失函数。 GAN的训…...