javap 字节码指令
转:原文:https://blog.csdn.net/w372426096/article/details/81664431
建议看原文,我粘贴的后面字节码指令集排版不太好。
一、javap命令简述
javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。
当然这些信息中,有些信息(如本地变量表、指令和代码行偏移量映射表、常量池中方法的参数名称等等)需要在使用javac编译成class文件时,指定参数才能输出,比如,你直接javac xx.java,就不会在生成对应的局部变量表等信息,如果你使用javac -g xx.java就可以生成所有相关信息了。如果你使用的eclipse,则默认情况下,eclipse在编译时会帮你生成局部变量表、指令和代码行偏移量映射表等信息的。
通过反编译生成的汇编代码,我们可以深入的了解java代码的工作机制。比如我们可以查看i++;这行代码实际运行时是先获取变量i的值,然后将这个值加1,最后再将加1后的值赋值给变量i。
通过局部变量表,我们可以查看局部变量的作用域范围、所在槽位等信息,甚至可以看到槽位复用等信息。
javap的用法格式:
javap
其中classes就是你要反编译的class文件。
在命令行中直接输入javap或javap -help可以看到javap的options有如下选项:
-hep —hep -? 输出此用法消息
-version 版本信息,其实是当前javap所在jdk的版本信息,不是cass在哪个jdk下生成的。
-v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
- 输出行号和本地变量表
-pubic 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类 和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-casspath &t;path> 指定查找用户类文件的位置
-bootcasspath &t;path> 覆盖引导类文件的位置
一般常用的是-v -l -c三个选项。
javap -v classxx,不仅会输出行号、本地变量表信息、反编译汇编代码,还会输出当前类用到的常量池等信息。
javap -l 会输出行号和本地变量表信息。
javap -c 会对当前class字节码进行反编译生成汇编代码。
查看汇编代码时,需要知道里面的jvm指令,可以参考官方文档:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html
另外通过jclasslib工具也可以看到上面这些信息,而且是可视化的,效果更好一些。
二、javap测试及内容详解
前面已经介绍过javap输出的内容有哪些,东西比较多,这里主要介绍其中code区(汇编指令)、局部变量表和代码行偏移映射三个部分。
如果需要分析更多的信息,可以使用javap -v进行查看。
另外,为了更方便理解,所有汇编指令不单拎出来讲解,而是在反汇编代码中以注释的方式讲解下面写段代码测试一下:
例子1:分析一下下面的代码反汇编之后结果:
public class TestDate {
private int count = 0;
public static void main(String[] args) {
TestDate testDate = new TestDate();
testDate.test1();
}
public void test1(){
Date date = new Date();
String name1 = “wangerbei”;
test2(date,name1);
System.out.println(date+name1);
}
public void test2(Date dateP,String name2){
dateP = null;
name2 = “zhangsan”;
}
public void test3(){
count++;
}
public void test4(){
int a = 0;
{
int b = 0;
b = a+1;
}
int c = a+1;
}
}
上面代码通过JAVAC -g 生成class文件,然后通过javap命令对字节码进行反汇编:
$ javap -c -l TestDate
得到下面内容(指令等部分是我参照着官方文档总结的):
Warning: Binary file TestDate contains com.justest.test.TestDate
Compiled from “TestDate.java”
public class com.justest.test.TestDate {
//默认的构造方法,在构造方法执行时主要完成一些初始化操作,包括一些成员变量的初始化赋值等操作
public com.justest.test.TestDate();
Code:
0: aload_0 //从本地变量表中加载索引为0的变量的值,也即this的引用,压入栈
1: invokespecial #10 //出栈,调用java/lang/Object.”
//,就是this指定的对象的init()方法完成初始化
4: aload_0 // 4到6表示,调用this.count = 0,也即为count复制为0。这里this引用入栈
5: iconst_0 //将常量0,压入到操作数栈
6: putfield //出栈前面压入的两个值(this引用,常量值0), 将0取出,并赋值给count
9: return
//指令与代码行数的偏移对应关系,每一行第一个数字对应代码行数,第二个数字对应前面code中指令前面的数字
LineNumberTable:
line 5: 0
line 7: 4
line 5: 9
//局部变量表,start+length表示这个变量在字节码中的生命周期起始和结束的偏移位置(this生命周期从头0到结尾10)
//,slot就是这个变量在局部变量表中的槽位(槽位可复用),name就是变量名称,Signatur局部变量类型描述
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/justest/test/TestDate;
public static void main(java.lang.String[]);
Code:
// new指令,创建一个class com/justest/test/TestDate对象,new指令并不能完全创建一个对象
//,对象只有在初,只有在调用初始化方法完成后(也就是调用了invokespecial指令之后),对象才创建成功,
0: new //创建对象,并将对象引用压入栈
3: dup //将操作数栈定的数据复制一份,并压入栈,此时栈中有两个引用值
4: invokespecial #20 //pop出栈引用值,调用其构造函数,完成对象的初始化
7: astore_1 //pop出栈引用值,将其(引用)赋值给局部变量表中的变量testDate
8: aload_1 //将testDate的引用值压入栈,因为testDate.test1();调用了testDate
//,这里使用aload_1从局部变量表中获得对应的变量testDate的值并压入操作数栈
9: invokevirtual #21 // Method test1:()V 引用出栈,调用testDate的test1()方法
12: return //整个main方法结束返回
LineNumberTable:
line 10: 0
line 11: 8
line 12: 12
//局部变量表,testDate只有在创建完成并赋值后,才开始声明周期
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 testDate Lcom/justest/test/TestDate;
public void test1();
Code:
0: new #27 // 0到7创建Date对象,并赋值给date变量
3: dup
4: invokespecial #29 // Method java/util/Date.”
7: astore_1
8: ldc #30 // String wangerbei,将常量“wangerbei”压入栈
10: astore_2 //将栈中的“wangerbei”pop出,赋值给name1
11: aload_0 //11到14,对应test2(date,name1);默认前面加this.
12: aload_1 //从局部变量表中取出date变量
13: aload_2 //取出name1变量
14: invokevirtual #32 // Method test2: (Ljava/util/Date;Ljava/lang/String;)V 调用test2方法
// 17到38对应System.out.println(date+name1);
17: getstatic #36 // Field java/lang/System.out:Ljava/io/PrintStream; //20到35是jvm中的优化手段,多个字符串变量相加
//,不会两两创建一个字符串对象
//,而使用StringBuilder来创建一个对象
20: new #42 // class java/lang/StringBuilder
23: dup
24: invokespecial #44 // Method java/lang/StringBuilder.”
27: aload_1
28: invokevirtual #45 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
31: aload_2
32: invokevirtual #49 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: invokevirtual #52 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
38: invokevirtual #56 // Method java/io/PrintStream.println:(Ljava/lang/String;)V invokevirtual指令表示基于类调用方法
41: return
LineNumberTable:
line 15: 0
line 16: 8
line 17: 11
line 18: 17
line 19: 41
LocalVariableTable:
Start Length Slot Name Signature
0 42 0 this Lcom/justest/test/TestDate;
8 34 1 date Ljava/util/Date;
11 31 2 name1 Ljava/lang/String;
public void test2(java.util.Date, java.lang.String);
Code:
0: aconst_null //将一个null值压入栈
1: astore_1 //将null赋值给dateP
2: ldc #66 // String zhangsan 从常量池中取出字符串“zhangsan”压入栈中
4: astore_2 //将字符串赋值给name2
5: return
LineNumberTable:
line 22: 0
line 23: 2
line 24: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/justest/test/TestDate;
0 6 1 dateP Ljava/util/Date;
0 6 2 name2 Ljava/lang/String;
public void test3();
Code:
0: aload_0 //取出this,压入栈
1: dup //复制操作数栈栈顶的值,并压入栈,此时有两个this对象引用值在操作数组栈
2: getfield #12// Field count:I this出栈,并获取其count字段,然后压入栈
//,此时栈中有一个this和一个count的值
5: iconst_1 //取出一个int常量1,压入操作数栈
6: iadd // 从栈中取出count和1,将count值和1相加,结果入栈
7: putfield #12 // Field count:I 一次弹出两个,第一个弹出的是上一步计算值
//,第二个弹出的this,将值赋值给this的count字段
10: return
LineNumberTable:
line 27: 0
line 28: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Lcom/justest/test/TestDate;
public void test4();
Code:
0: iconst_0
1: istore_1
2: iconst_0
3: istore_2
4: iload_1
5: iconst_1
6: iadd
7: istore_2
8: iload_1
9: iconst_1
10: iadd
11: istore_2
12: return LineNumberTable: line
33: 0 line
35: 2 line
36: 4 line
38: 8 line
39: 12
//看下面,b和c的槽位slot一样,这是因为b的作用域就在方法块中,方法块结束
//,局部变量表中的槽位就被释放,后面的变量就可以复用这个槽位
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 this Lcom/justest/test/TestDate;
2 11 1 a I
4 4 2 b I
12 1 2 c I
}
例子2:下面一个例子
先有一个User类:
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后写一个操作User对象的测试类:
public class TestUser {
private int count;
public void test(int a){
count = count + a;
}
public User initUser(int age,String name){
User user = new User();
user.setAge(age);
user.setName(name);
return user;
}
public void changeUser(User user,String newName){
user.setName(newName);
}
}
先javac -g 编译成class文件。
然后对TestUser类进行反汇编:
$ javap -c -l TestUser
得到反汇编结果如下:
Warning: Binary file TestUser contains com.justest.test.TestUser
Compiled from “TestUser.java”
public class com.justest.test.TestUser {
//默认的构造函数
public com.justest.test.TestUser();
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object.”
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/justest/test/TestUser;
public void test(int);
Code:
0: aload_0 //取this对应的对应引用值,压入操作数栈
1: dup //复制栈顶的数据,压入栈,此时栈中有两个值,都是this对象引用
2: getfield #18 // 引用出栈,通过引用获得对应count的值,并压入栈
5: iload_1 //从局部变量表中取得a的值,压入栈中
6: iadd //弹出栈中的count值和a的值,进行加操作,并将结果压入栈
7: putfield #18 // 经过上一步操作后,栈中有两个值,栈顶为上一步操作结果,栈顶下面是this引用,这一步putfield指令,用于将栈顶的值赋值给引用对象的count字段
10: return //return void
LineNumberTable:
line 8: 0
line 9: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Lcom/justest/test/TestUser;
0 11 1 a I
public com.justest.test.User initUser(int, java.lang.String);
Code:
0: new #23 // class com/justest/test/User 创建User对象,并将引用压入栈
3: dup //复制栈顶值,再次压入栈,栈中有两个User对象的地址引用 4: invokespecial #25 // Method com/justest/test/User.”
7: astore_3 //从栈中pop出User对象的引用值,并赋值给局部变量表中user变量
8: aload_3 //从局部变量表中获得user的值,也就是User对象的地址引用,压入栈中
9: iload_1 //从局部变量表中获得a的值,并压入栈中,注意aload和iload的区别,一个取值是对象引用,一个是取int类型数据
10: invokevirtual #26 // Method com/justest/test/User.setAge:(I)V 操作数栈pop出两个值,一个是User对象引用,一个是a的值,调用setAge方法,并将a的值传给这个方法,setAge操作的就是堆中对象的字段了
13: aload_3 //同7,压入栈 14: aload_2 //从局部变量表取出name,压入栈
15: invokevirtual #29 //MethodUser.setName:(Ljava/lang/String;)V 操作数栈pop出两个值,一个是User对象引用,一个是name的值,调用setName方法,并将a的值传给这个方法,setName操作的就是堆中对象的字段了
18: aload_3 //从局部变量取出User引用,压入栈
19: areturn //areturn指令用于返回一个对象的引用,也就是上一步中User的引用,这个返回值将会被压入调用当前方法的那个方法的栈中objectref is popped from the operand stack of the current frame ([§2.6](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html\#jvms-2.6)) and pushed onto the operand stack of the frame of the invoker
LineNumberTable:
line 12: 0
line 13: 8
line 14: 13
line 15: 18
LocalVariableTable:
Start Length Slot Name Signature
0 20 0 this Lcom/justest/test/TestUser;
0 20 1 age I
0 20 2 name Ljava/lang/String;
8 12 3 user Lcom/justest/test/User;
public void changeUser(com.justest.test.User, java.lang.String);
Code:
0: aload_1 //局部变量表中取出this,也即TestUser对象引用,压入栈 1: aload_2 //局部变量表中取出newName,压入栈
2: invokevirtual #29 // Method User.setName:(Ljava/lang/String;)V pop出栈newName值和TestUser引用,调用其setName方法,并将newName的值传给这个方法
5: return
LineNumberTable:
line 19: 0
line 20: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/justest/test/TestUser;
0 6 1 user Lcom/justest/test/User;
0 6 2 newName Ljava/lang/String;
public static void main(java.lang.String[]);
Code:
0: new #1 // class com/justest/test/TestUser 创建TestUser对象,将引用压入栈
3: dup //复制引用,压入栈
4: invokespecial #43 // Method “
7: astore_1 //引用值出栈,赋值给局部变量表中变量tu
8: aload_1 //取出tu值,压入栈
9: bipush 10 //将int值10压入栈
11: ldc #44 // String wangerbei 从常量池中取出“wangerbei” 压入栈
13: invokevirtual #46 // Method initUser(ILjava/lang/String;)Lcom/justest/test/User; 调用tu的initUser方法,并返回User对象 ,出栈三个值:tu引用,10和“wangerbei”,并且initUser方法的返回值,即User的引用,也会被压入栈中,参考前面initUser中的areturn指令
16: astore_2 //User引用出栈,赋值给user变量
17: aload_1 //取出tu值,压入栈
18: aload_2 //取出user值,压入栈
19: ldc #48 // String lisi 从常量池中取出“lisi”压入栈
21: invokevirtual #50 // Method changeUser:(Lcom/justest/test/User;Ljava/lang/String;)V 调用tu的changeUser方法,并将user引用和lisi传给这个方法
24: return //return void
LineNumberTable:
line 23: 0
line 24: 8
line 25: 17
line 26: 24
LocalVariableTable:
Start Length Slot Name Signature
0 25 0 args [Ljava/lang/String;
8 17 1 tu Lcom/justest/test/TestUser;
17 8 2 user Lcom/justest/test/User;
}
三、总结
1、通过javap命令可以查看一个java类反汇编、常量池、变量表、指令代码行号表等等信息。
2、平常,我们比较关注的是java类中每个方法的反汇编中的指令操作过程,这些指令都是顺序执行的,可以参考官方文档查看每个指令的含义,很简单:
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html\#jvms-6.5.areturn
3、通过对前面两个例子代码反汇编中各个指令操作的分析,可以发现,一个方法的执行通常会涉及下面几块内存的操作:
(1)java栈中:局部变量表、操作数栈。这些操作基本上都值操作。
(2)java堆。通过对象的地址引用去操作。
(3)常量池。
(4)其他如帧数据区、方法区(jdk1.8之前,常量池也在方法区)等部分,测试中没有显示出来,这里说明一下。
在做值相关操作时:
一个指令,可以从局部变量表、常量池、堆中对象、方法调用、系统调用中等取得数据,这些数据(可能是指,可能是对象的引用)被压入操作数栈。
一个指令,也可以从操作数数栈中取出一到多个值(pop多次),完成赋值、加减乘除、方法传参、系统调用等等操作。
扩展:
JVM字节码之整型入栈指令(iconst、bipush、sipush、ldc)
JVM中int类型数值采用何种指令入栈的,根据int值范围JVM入栈字节码指令就分为4类,下面分别介绍下这四类指令。
当int取值-1~5采用iconst指令,取值-128~127采用bipush指令,取值-32768~32767采用sipush指令,取值-2147483648~2147483647采用 ldc 指令。
iconst
当int取值-1~5时,JVM采用iconst指令将常量压入栈中。
定义Test.java文件
public static void main(String[] args) {
int i = 5;
int j = -1;
}
查看class文件
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1
2: iconst_m1
3: istore_2
4: return
}
分析class文件,int取值0~5时JVM采用iconst_0、iconst_1、iconst_2、iconst_3、iconst_4、iconst_5指令将常量压入栈中,取值-1时采用iconst_m1指令将常量压入栈中。
bipush
当int取值-128~127时,JVM采用bipush指令将常量压入栈中。
定义Test.java文件
public static void main(String[] args) {
int i = 127;
}
查看class文件
public static void main(java.lang.String[]);
Code:
0: bipush 127
2: istore_1
3: return
}
可以看到上面代码第三行是采用bipush指令将常量127压入栈中。
sipush
当int取值-32768~32767时,JVM采用sipush指令将常量压入栈中。
定义Test.java文
public static void main(String[] args) {
int i = 32767;
}
查看class文
public static void main(java.lang.String[]);
Code:
0: sipush 32767
3: istore_1
4: return
}
可以看到上面代码第三行是采用sipush指令将常量32767压入栈中。
ldc
当int取值-2147483648~2147483647时,JVM采用ldc指令将常量压入栈中。
定义Test.java文
public static void main(String[] args) {
int i = Integer.MAX_VALUE;
}
查看class文件
public static void main(java.lang.String[]);
Code:
0: ldc #2; //int 2147483647
2: istore_1
3: return
}
可以看到上面代码第三行是采用ldc指令将2147483647常量压入栈中,需要注意的是ldc指令是从常量池中获取值的,也就是说在这段范围(-2147483648~2147483647)内的int值是存储在常量池中的。
JVM字节码之简单解读
public static void main(String[] args) {
int i = 100;
int j = 200 + i - 100;
int z = j++;
System.out.println(z+i);
}
上面源码执行之后的结果是300,下面我们就通过分析class来看看结果是怎么得来的。
Class字节码
public static void main(java.lang.String[]);
Code:
Stack=3, Locals=4, Args_size=1
0: bipush 100
2: istore_1
3: sipush 200
6: iload_1
7: iadd
8: bipush 100
10: isub
11: istore_2
12: iload_2
13: iinc 2, 1
16: istore_3
17: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
20: iload_3
21: iload_1
22: iadd
23: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
26: return
}
下面我们就来分析下字节码的执行:
Stack=3, Locals=4, Args_size=1,这段说明栈中元素有3个,局部变量表中有元素4个,参数1个
0: bipush 100 # 将常量100压入栈中
2: istore_1 # 从栈中取出常量100存储到局部变量表中,下标索引为1
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_01.png-hunterblog
3: sipush 200 # 将常量200压入栈中
6: iload_1 # 将下标索引为1的常量从局部变量表中压入栈中。
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_02.png-hunterblog
7: iadd # 从栈中取出两个整型常量相加并将结果存储到栈中
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_03.png-hunterblog
8: bipush 100 # 将常量100压入栈中
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_04.png-hunterblog
10: isub # 从栈中取出两个整型常量做相减并将结果存储到栈中
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_05.png-hunterblog
11: istore_2 # 从栈中取出常量存储到局部变量表中,下标索引为2
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_06.png-hunterblog
12: iload_2 # 将下标索引为2的常量从局部变量表中压入栈中
13: iinc 2, 1 # 将局部变量表中下标索引为2的变量自增。
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_07.png-hunterblog
16: istore_3 # 从栈中取出常量存储到局部变量表中,下标索引为3
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_08.png-hunterblog
17: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
20: iload_3 # 将下标索引为3的常量从局部变量表中压入栈中
21: iload_1 # 将下标索引为1的常量从局部变量表中压入栈中
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_09.png-hunterblog
22: iadd # 从栈中取出两个整型常量相加并将结果存储到栈中
http://7xqlat.com1.z0.glb.clouddn.com/jvm\_read\_class\_stack\_10.png-hunterblog
23: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
26: return
所以最后执行结果是300
大家可能注意到了,在字节码第三行Stack=3, Locals=4, Args_size=1,显示的是栈中有3个元素,为什么我们这里只有两个?其实栈中还有一个this元素,由于this不在本篇介绍范围之类,所以例图就省略了,特此说明下。大家可能疑惑这个this是什么时候入栈的,通过下面的代码相信大家就明白
public test.Test();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object.”
4: return
LineNumberTable:
line 3: 0
参考
Java字节码指令列表
http://www.linmuxi.com/2016/02/26/jvm-class-read-01/
https://www.jianshu.com/p/6a8997560b05
附录
指令码
助记符
说明
0x00
nop
什么都不做
0x01
aconst_null
将null推送至栈顶
0x02
iconst_m1
将int型-1推送至栈顶
0x03
iconst_0
将int型0推送至栈顶
0x04
iconst_1
将int型1推送至栈顶
0x05
iconst_2
将int型2推送至栈顶
0x06
iconst_3
将int型3推送至栈顶
0x07
iconst_4
将int型4推送至栈顶
0x08
iconst_5
将int型5推送至栈顶
0x09
lconst_0
将long型0推送至栈顶
0x0a
lconst_1
将long型1推送至栈顶
0x0b
fconst_0
将float型0推送至栈顶
0x0c
fconst_1
将float型1推送至栈顶
0x0d
fconst_2
将float型2推送至栈顶
0x0e
dconst_0
将double型0推送至栈顶
0x0f
dconst_1
将double型1推送至栈顶
0x10
bipush
将单字节的常量值(-128~127)推送至栈顶
0x11
sipush
将一个短整型常量值(-32768~32767)推送至栈顶
0x12
ldc
将int, float或String型常量值从常量池中推送至栈顶
0x13
ldc_w
将int, float或String型常量值从常量池中推送至栈顶(宽索引)
0x14
ldc2_w
将long或double型常量值从常量池中推送至栈顶(宽索引)
0x15
iload
将指定的int型本地变量推送至栈顶
0x16
lload
将指定的long型本地变量推送至栈顶
0x17
fload
将指定的float型本地变量推送至栈顶
0x18
dload
将指定的double型本地变量推送至栈顶
0x19
aload
将指定的引用类型本地变量推送至栈顶
0x1a
iload_0
将第一个int型本地变量推送至栈顶
0x1b
iload_1
将第二个int型本地变量推送至栈顶
0x1c
iload_2
将第三个int型本地变量推送至栈顶
0x1d
iload_3
将第四个int型本地变量推送至栈顶
0x1e
lload_0
将第一个long型本地变量推送至栈顶
0x1f
lload_1
将第二个long型本地变量推送至栈顶
0x20
lload_2
将第三个long型本地变量推送至栈顶
0x21
lload_3
将第四个long型本地变量推送至栈顶
0x22
fload_0
将第一个float型本地变量推送至栈顶
0x23
fload_1
将第二个float型本地变量推送至栈顶
0x24
fload_2
将第三个float型本地变量推送至栈顶
0x25
fload_3
将第四个float型本地变量推送至栈顶
0x26
dload_0
将第一个double型本地变量推送至栈顶
0x27
dload_1
将第二个double型本地变量推送至栈顶
0x28
dload_2
将第三个double型本地变量推送至栈顶
0x29
dload_3
将第四个double型本地变量推送至栈顶
0x2a
aload_0
将第一个引用类型本地变量推送至栈顶
0x2b
aload_1
将第二个引用类型本地变量推送至栈顶
0x2c
aload_2
将第三个引用类型本地变量推送至栈顶
0x2d
aload_3
将第四个引用类型本地变量推送至栈顶
0x2e
iaload
将int型数组指定索引的值推送至栈顶
0x2f
laload
将long型数组指定索引的值推送至栈顶
0x30
faload
将float型数组指定索引的值推送至栈顶
0x31
daload
将double型数组指定索引的值推送至栈顶
0x32
aaload
将引用型数组指定索引的值推送至栈顶
0x33
baload
将boolean或byte型数组指定索引的值推送至栈顶
0x34
caload
将char型数组指定索引的值推送至栈顶
0x35
saload
将short型数组指定索引的值推送至栈顶
0x36
istore
将栈顶int型数值存入指定本地变量
0x37
lstore
将栈顶long型数值存入指定本地变量
0x38
fstore
将栈顶float型数值存入指定本地变量
0x39
dstore
将栈顶double型数值存入指定本地变量
0x3a
astore
将栈顶引用型数值存入指定本地变量
0x3b
istore_0
将栈顶int型数值存入第一个本地变量
0x3c
istore_1
将栈顶int型数值存入第二个本地变量
0x3d
istore_2
将栈顶int型数值存入第三个本地变量
0x3e
istore_3
将栈顶int型数值存入第四个本地变量
0x3f
lstore_0
将栈顶long型数值存入第一个本地变量
0x40
lstore_1
将栈顶long型数值存入第二个本地变量
0x41
lstore_2
将栈顶long型数值存入第三个本地变量
0x42
lstore_3
将栈顶long型数值存入第四个本地变量
0x43
fstore_0
将栈顶float型数值存入第一个本地变量
0x44
fstore_1
将栈顶float型数值存入第二个本地变量
0x45
fstore_2
将栈顶float型数值存入第三个本地变量
0x46
fstore_3
将栈顶float型数值存入第四个本地变量
0x47
dstore_0
将栈顶double型数值存入第一个本地变量
0x48
dstore_1
将栈顶double型数值存入第二个本地变量
0x49
dstore_2
将栈顶double型数值存入第三个本地变量
0x4a
dstore_3
将栈顶double型数值存入第四个本地变量
0x4b
astore_0
将栈顶引用型数值存入第一个本地变量
0x4c
astore_1
将栈顶引用型数值存入第二个本地变量
0x4d
astore_2
将栈顶引用型数值存入第三个本地变量
0x4e
astore_3
将栈顶引用型数值存入第四个本地变量
0x4f
iastore
将栈顶int型数值存入指定数组的指定索引位置
0x50
lastore
将栈顶long型数值存入指定数组的指定索引位置
0x51
fastore
将栈顶float型数值存入指定数组的指定索引位置
0x52
dastore
将栈顶double型数值存入指定数组的指定索引位置
0x53
aastore
将栈顶引用型数值存入指定数组的指定索引位置
0x54
bastore
将栈顶boolean或byte型数值存入指定数组的指定索引位置
0x55
castore
将栈顶char型数值存入指定数组的指定索引位置
0x56
sastore
将栈顶short型数值存入指定数组的指定索引位置
0x57
pop
将栈顶数值弹出 (数值不能是long或double类型的)
0x58
pop2
将栈顶的一个(long或double类型的)或两个数值弹出(其它)
0x59
dup
复制栈顶数值并将复制值压入栈顶
0x5a
dup_x1
复制栈顶数值并将两个复制值压入栈顶
0x5b
dup_x2
复制栈顶数值并将三个(或两个)复制值压入栈顶
0x5c
dup2
复制栈顶一个(long或double类型的)或两个(其它)数值并将复制值压入栈顶
0x5d
dup2_x1
<待补充>
0x5e
dup2_x2
<待补充>
0x5f
swap
将栈最顶端的两个数值互换(数值不能是long或double类型的)
0x60
iadd
将栈顶两int型数值相加并将结果压入栈顶
0x61
ladd
将栈顶两long型数值相加并将结果压入栈顶
0x62
fadd
将栈顶两float型数值相加并将结果压入栈顶
0x63
dadd
将栈顶两double型数值相加并将结果压入栈顶
0x64
isub
将栈顶两int型数值相减并将结果压入栈顶
0x65
lsub
将栈顶两long型数值相减并将结果压入栈顶
0x66
fsub
将栈顶两float型数值相减并将结果压入栈顶
0x67
dsub
将栈顶两double型数值相减并将结果压入栈顶
0x68
imul
将栈顶两int型数值相乘并将结果压入栈顶
0x69
lmul
将栈顶两long型数值相乘并将结果压入栈顶
0x6a
fmul
将栈顶两float型数值相乘并将结果压入栈顶
0x6b
dmul
将栈顶两double型数值相乘并将结果压入栈顶
0x6c
idiv
将栈顶两int型数值相除并将结果压入栈顶
0x6d
ldiv
将栈顶两long型数值相除并将结果压入栈顶
0x6e
fdiv
将栈顶两float型数值相除并将结果压入栈顶
0x6f
ddiv
将栈顶两double型数值相除并将结果压入栈顶
0x70
irem
将栈顶两int型数值作取模运算并将结果压入栈顶
0x71
lrem
将栈顶两long型数值作取模运算并将结果压入栈顶
0x72
frem
将栈顶两float型数值作取模运算并将结果压入栈顶
0x73
drem
将栈顶两double型数值作取模运算并将结果压入栈顶
0x74
ineg
将栈顶int型数值取负并将结果压入栈顶
0x75
lneg
将栈顶long型数值取负并将结果压入栈顶
0x76
fneg
将栈顶float型数值取负并将结果压入栈顶
0x77
dneg
将栈顶double型数值取负并将结果压入栈顶
0x78
ishl
将int型数值左移位指定位数并将结果压入栈顶
0x79
lshl
将long型数值左移位指定位数并将结果压入栈顶
0x7a
ishr
将int型数值右(符号)移位指定位数并将结果压入栈顶
0x7b
lshr
将long型数值右(符号)移位指定位数并将结果压入栈顶
0x7c
iushr
将int型数值右(无符号)移位指定位数并将结果压入栈顶
0x7d
lushr
将long型数值右(无符号)移位指定位数并将结果压入栈顶
0x7e
iand
将栈顶两int型数值作“按位与”并将结果压入栈顶
0x7f
land
将栈顶两long型数值作“按位与”并将结果压入栈顶
0x80
ior
将栈顶两int型数值作“按位或”并将结果压入栈顶
0x81
lor
将栈顶两long型数值作“按位或”并将结果压入栈顶
0x82
ixor
将栈顶两int型数值作“按位异或”并将结果压入栈顶
0x83
lxor
将栈顶两long型数值作“按位异或”并将结果压入栈顶
0x84
iinc
将指定int型变量增加指定值(i++, i—, i+=2)
0x85
i2l
将栈顶int型数值强制转换成long型数值并将结果压入栈顶
0x86
i2f
将栈顶int型数值强制转换成float型数值并将结果压入栈顶
0x87
i2d
将栈顶int型数值强制转换成double型数值并将结果压入栈顶
0x88
l2i
将栈顶long型数值强制转换成int型数值并将结果压入栈顶
0x89
l2f
将栈顶long型数值强制转换成float型数值并将结果压入栈顶
0x8a
l2d
将栈顶long型数值强制转换成double型数值并将结果压入栈顶
0x8b
f2i
将栈顶float型数值强制转换成int型数值并将结果压入栈顶
0x8c
f2l
将栈顶float型数值强制转换成long型数值并将结果压入栈顶
0x8d
f2d
将栈顶float型数值强制转换成double型数值并将结果压入栈顶
0x8e
d2i
将栈顶double型数值强制转换成int型数值并将结果压入栈顶
0x8f
d2l
将栈顶double型数值强制转换成long型数值并将结果压入栈顶
0x90
d2f
将栈顶double型数值强制转换成float型数值并将结果压入栈顶
0x91
i2b
将栈顶int型数值强制转换成byte型数值并将结果压入栈顶
0x92
i2c
将栈顶int型数值强制转换成char型数值并将结果压入栈顶
0x93
i2s
将栈顶int型数值强制转换成short型数值并将结果压入栈顶
0x94
lcmp
比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶
0x95
fcmpl
比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x96
fcmpg
比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x97
dcmpl
比较栈顶两double型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x98
dcmpg
比较栈顶两double型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x99
ifeq
当栈顶int型数值等于0时跳转
0x9a
ifne
当栈顶int型数值不等于0时跳转
0x9b
iflt
当栈顶int型数值小于0时跳转
0x9c
ifge
当栈顶int型数值大于等于0时跳转
0x9d
ifgt
当栈顶int型数值大于0时跳转
0x9e
ifle
当栈顶int型数值小于等于0时跳转
0x9f
if_icmpeq
比较栈顶两int型数值大小,当结果等于0时跳转
0xa0
if_icmpne
比较栈顶两int型数值大小,当结果不等于0时跳转
0xa1
if_icmplt
比较栈顶两int型数值大小,当结果小于0时跳转
0xa2
if_icmpge
比较栈顶两int型数值大小,当结果大于等于0时跳转
0xa3
if_icmpgt
比较栈顶两int型数值大小,当结果大于0时跳转
0xa4
if_icmple
比较栈顶两int型数值大小,当结果小于等于0时跳转
0xa5
if_acmpeq
比较栈顶两引用型数值,当结果相等时跳转
0xa6
if_acmpne
比较栈顶两引用型数值,当结果不相等时跳转
0xa7
goto
无条件跳转
0xa8
jsr
跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶
0xa9
ret
返回至本地变量指定的index的指令位置(一般与jsr, jsr_w联合使用)
0xaa
tableswitch
用于switch条件跳转,case值连续(可变长度指令)
0xab
lookupswitch
用于switch条件跳转,case值不连续(可变长度指令)
0xac
ireturn
从当前方法返回int
0xad
lreturn
从当前方法返回long
0xae
freturn
从当前方法返回float
0xaf
dreturn
从当前方法返回double
0xb0
areturn
从当前方法返回对象引用
0xb1
return
从当前方法返回void
0xb2
getstatic
获取指定类的静态域,并将其值压入栈顶
0xb3
putstatic
为指定的类的静态域赋值
0xb4
getfield
获取指定类的实例域,并将其值压入栈顶
0xb5
putfield
为指定的类的实例域赋值
0xb6
invokevirtual
调用实例方法
0xb7
invokespecial
调用超类构造方法,实例初始化方法,私有方法
0xb8
invokestatic
调用静态方法
0xb9
invokeinterface
调用接口方法
0xba
--
0xbb
new
创建一个对象,并将其引用值压入栈顶
0xbc
newarray
创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶
0xbd
anewarray
创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶
0xbe
arraylength
获得数组的长度值并压入栈顶
0xbf
athrow
将栈顶的异常抛出
0xc0
checkcast
检验类型转换,检验未通过将抛出ClassCastException
0xc1
instanceof
检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶
0xc2
monitorenter
获得对象的锁,用于同步方法或同步块
0xc3
monitorexit
释放对象的锁,用于同步方法或同步块
0xc4
wide
<待补充>
0xc5
multianewarray
创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶
0xc6
ifnull
为null时跳转
0xc7
ifnonnull
不为null时跳转
0xc8
goto_w
无条件跳转(宽索引)
0xc9
jsr_w
跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶
-——————————
作者:Franco蜡笔小强
来源:CSDN
原文:https://blog.csdn.net/w372426096/article/details/81664431
版权声明:本文为博主原创文章,转载请附上博文链接!
还没有评论,来说两句吧...