如下,是一些java字节码也就是原始的class文件,当应用部署到线上之后,我们能够看到的也就是这样的字样了。那么怎样解呢?就让我们一起,来解读解读字节码吧!
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 CA FE BA BE 00 00 00 34 00 6A 0A 00 1C 00 39 0A 漱壕 4 j 9 00000010 00 13 00 3A 09 00 13 00 3B 09 00 3C 00 3D 08 00 : ; < = 00000020 3E 0A 00 3F 00 40 07 00 41 0A 00 07 00 39 09 00 > ? @ A 9 00000030 13 00 42 07 00 43 0A 00 0A 00 39 08 00 44 0A 00 B C 9 D 00000040 0A 00 45 0A 00 46 00 47 0A 00 0A 00 48 08 00 49 E F G H I 00000050 0A 00 0A 00 4A 0B 00 4B 00 4C 07 00 4D 0A 00 13 J K L M 00000060 00 39 0A 00 13 00 4E 07 00 4F 0A 00 16 00 39 08 9 N O 9 00000070 00 50 0A 00 16 00 51 0A 00 16 00 52 0A 00 16 00 P Q R 00000080 53 07 00 54 01 00 0B 75 73 65 72 53 65 72 76 69 S T userServi 00000090 63 65 01 00 24 4C 63 6F 6D 2F 79 6F 75 67 65 2F ce $Lcom/youge/ 000000A0 73 65 72 76 69 63 65 2F 75 73 65 72 2F 55 73 65 service/user/Use 000000B0 72 53 65 72 76 69 63 65 3B 01 00 09 69 6E 69 74 rService; init 000000C0 69 61 6C 65 64 01 00 01 5A 01 00 06 3C 69 6E 69 ialed Z <ini 000000D0 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 t> ()V Code
原始类源码如下:
package com.youge.api; import com.youge.pojo.user.UserInfo; import com.youge.service.user.UserService; import com.youge.service.user.UserServiceImpl; public class ByteCodeClassKen { private UserService userService; private boolean initialed; public ByteCodeClassKen() { init(); } private void init() { if(!initialed) { System.out.println("init..."); userService = new UserServiceImpl(); initialed = true; } } public Integer addUser() throws Exception { UserInfo userInfo = new UserInfo(); userInfo.setName("lilei"); userInfo.setAge(12); userInfo.setAddress("new road."); return userService.addUser(userInfo); } public static void main(String[] args) throws Exception { // test ByteCodeClassKen classKen = new ByteCodeClassKen(); Integer affect = classKen.addUser(); System.out.println("affect: " + affect); } }
完整字节码文件,如有兴趣请展开:
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F 00000000 CA FE BA BE 00 00 00 34 00 6A 0A 00 1C 00 39 0A 漱壕 4 j 9 00000010 00 13 00 3A 09 00 13 00 3B 09 00 3C 00 3D 08 00 : ; < = 00000020 3E 0A 00 3F 00 40 07 00 41 0A 00 07 00 39 09 00 > ? @ A 9 00000030 13 00 42 07 00 43 0A 00 0A 00 39 08 00 44 0A 00 B C 9 D 00000040 0A 00 45 0A 00 46 00 47 0A 00 0A 00 48 08 00 49 E F G H I 00000050 0A 00 0A 00 4A 0B 00 4B 00 4C 07 00 4D 0A 00 13 J K L M 00000060 00 39 0A 00 13 00 4E 07 00 4F 0A 00 16 00 39 08 9 N O 9 00000070 00 50 0A 00 16 00 51 0A 00 16 00 52 0A 00 16 00 P Q R 00000080 53 07 00 54 01 00 0B 75 73 65 72 53 65 72 76 69 S T userServi 00000090 63 65 01 00 24 4C 63 6F 6D 2F 79 6F 75 67 65 2F ce $Lcom/youge/ 000000A0 73 65 72 76 69 63 65 2F 75 73 65 72 2F 55 73 65 service/user/Use 000000B0 72 53 65 72 76 69 63 65 3B 01 00 09 69 6E 69 74 rService; init 000000C0 69 61 6C 65 64 01 00 01 5A 01 00 06 3C 69 6E 69 ialed Z <ini 000000D0 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 t> ()V Code 000000E0 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C LineNumberTabl 000000F0 65 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C e LocalVariabl 00000100 65 54 61 62 6C 65 01 00 04 74 68 69 73 01 00 20 eTable this 00000110 4C 63 6F 6D 2F 79 6F 75 67 65 2F 61 70 69 2F 42 Lcom/youge/api/B 00000120 79 74 65 43 6F 64 65 43 6C 61 73 73 4B 65 6E 3B yteCodeClassKen; 00000130 01 00 04 69 6E 69 74 01 00 0D 53 74 61 63 6B 4D init StackM 00000140 61 70 54 61 62 6C 65 01 00 07 61 64 64 55 73 65 apTable addUse 00000150 72 01 00 15 28 29 4C 6A 61 76 61 2F 6C 61 6E 67 r ()Ljava/lang 00000160 2F 49 6E 74 65 67 65 72 3B 01 00 08 75 73 65 72 /Integer; user 00000170 49 6E 66 6F 01 00 1E 4C 63 6F 6D 2F 79 6F 75 67 Info Lcom/youg 00000180 65 2F 70 6F 6A 6F 2F 75 73 65 72 2F 55 73 65 72 e/pojo/user/User 00000190 49 6E 66 6F 3B 01 00 0A 45 78 63 65 70 74 69 6F Info; Exceptio 000001A0 6E 73 07 00 55 01 00 04 6D 61 69 6E 01 00 16 28 ns U main ( 000001B0 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 [Ljava/lang/Stri 000001C0 6E 67 3B 29 56 01 00 04 61 72 67 73 01 00 13 5B ng;)V args [ 000001D0 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E Ljava/lang/Strin 000001E0 67 3B 01 00 08 63 6C 61 73 73 4B 65 6E 01 00 06 g; classKen 000001F0 61 66 66 65 63 74 01 00 13 4C 6A 61 76 61 2F 6C affect Ljava/l 00000200 61 6E 67 2F 49 6E 74 65 67 65 72 3B 01 00 0A 53 ang/Integer; S 00000210 6F 75 72 63 65 46 69 6C 65 01 00 15 42 79 74 65 ourceFile Byte 00000220 43 6F 64 65 43 6C 61 73 73 4B 65 6E 2E 6A 61 76 CodeClassKen.jav 00000230 61 0C 00 21 00 22 0C 00 28 00 22 0C 00 1F 00 20 a ! " ( " 00000240 07 00 56 0C 00 57 00 58 01 00 07 69 6E 69 74 2E V W X init. 00000250 2E 2E 07 00 59 0C 00 5A 00 5B 01 00 26 63 6F 6D .. Y Z [ &com 00000260 2F 79 6F 75 67 65 2F 73 65 72 76 69 63 65 2F 75 /youge/service/u 00000270 73 65 72 2F 55 73 65 72 53 65 72 76 69 63 65 49 ser/UserServiceI 00000280 6D 70 6C 0C 00 1D 00 1E 01 00 1C 63 6F 6D 2F 79 mpl com/y 00000290 6F 75 67 65 2F 70 6F 6A 6F 2F 75 73 65 72 2F 55 ouge/pojo/user/U 000002A0 73 65 72 49 6E 66 6F 01 00 05 6C 69 6C 65 69 0C serInfo lilei 000002B0 00 5C 00 5B 07 00 5D 0C 00 5E 00 5F 0C 00 60 00 \ [ ] ^ _ ` 000002C0 61 01 00 09 6E 65 77 20 72 6F 61 64 2E 0C 00 62 a new road. b 000002D0 00 5B 07 00 63 0C 00 2A 00 64 01 00 1E 63 6F 6D [ c * d com 000002E0 2F 79 6F 75 67 65 2F 61 70 69 2F 42 79 74 65 43 /youge/api/ByteC 000002F0 6F 64 65 43 6C 61 73 73 4B 65 6E 0C 00 2A 00 2B odeClassKen * + 00000300 01 00 17 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 java/lang/Str 00000310 69 6E 67 42 75 69 6C 64 65 72 01 00 08 61 66 66 ingBuilder aff 00000320 65 63 74 3A 20 0C 00 65 00 66 0C 00 65 00 67 0C ect: e f e g 00000330 00 68 00 69 01 00 10 6A 61 76 61 2F 6C 61 6E 67 h i java/lang 00000340 2F 4F 62 6A 65 63 74 01 00 13 6A 61 76 61 2F 6C /Object java/l 00000350 61 6E 67 2F 45 78 63 65 70 74 69 6F 6E 01 00 10 ang/Exception 00000360 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D java/lang/System 00000370 01 00 03 6F 75 74 01 00 15 4C 6A 61 76 61 2F 69 out Ljava/i 00000380 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B 01 00 o/PrintStream; 00000390 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 java/io/PrintSt 000003A0 72 65 61 6D 01 00 07 70 72 69 6E 74 6C 6E 01 00 ream println 000003B0 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 (Ljava/lang/Str 000003C0 69 6E 67 3B 29 56 01 00 07 73 65 74 4E 61 6D 65 ing;)V setName 000003D0 01 00 11 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 java/lang/Int 000003E0 65 67 65 72 01 00 07 76 61 6C 75 65 4F 66 01 00 eger valueOf 000003F0 16 28 49 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 (I)Ljava/lang/I 00000400 6E 74 65 67 65 72 3B 01 00 06 73 65 74 41 67 65 nteger; setAge 00000410 01 00 16 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 (Ljava/lang/I 00000420 6E 74 65 67 65 72 3B 29 56 01 00 0A 73 65 74 41 nteger;)V setA 00000430 64 64 72 65 73 73 01 00 22 63 6F 6D 2F 79 6F 75 ddress "com/you 00000440 67 65 2F 73 65 72 76 69 63 65 2F 75 73 65 72 2F ge/service/user/ 00000450 55 73 65 72 53 65 72 76 69 63 65 01 00 33 28 4C UserService 3(L 00000460 63 6F 6D 2F 79 6F 75 67 65 2F 70 6F 6A 6F 2F 75 com/youge/pojo/u 00000470 73 65 72 2F 55 73 65 72 49 6E 66 6F 3B 29 4C 6A ser/UserInfo;)Lj 00000480 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 ava/lang/Integer 00000490 3B 01 00 06 61 70 70 65 6E 64 01 00 2D 28 4C 6A ; append -(Lj 000004A0 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B ava/lang/String; 000004B0 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 )Ljava/lang/Stri 000004C0 6E 67 42 75 69 6C 64 65 72 3B 01 00 2D 28 4C 6A ngBuilder; -(Lj 000004D0 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 3B ava/lang/Object; 000004E0 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 )Ljava/lang/Stri 000004F0 6E 67 42 75 69 6C 64 65 72 3B 01 00 08 74 6F 53 ngBuilder; toS 00000500 74 72 69 6E 67 01 00 14 28 29 4C 6A 61 76 61 2F tring ()Ljava/ 00000510 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 00 21 00 13 lang/String; ! 00000520 00 1C 00 00 00 02 00 02 00 1D 00 1E 00 00 00 02 00000530 00 1F 00 20 00 00 00 04 00 01 00 21 00 22 00 01 ! " 00000540 00 23 00 00 00 3B 00 01 00 01 00 00 00 09 2A B7 # ; *? 00000550 00 01 2A B7 00 02 B1 00 00 00 02 00 24 00 00 00 *? ? $ 00000560 0E 00 03 00 00 00 12 00 04 00 13 00 08 00 14 00 00000570 25 00 00 00 0C 00 01 00 00 00 09 00 26 00 27 00 % & ' 00000580 00 00 02 00 28 00 22 00 01 00 23 00 00 00 63 00 ( " # c 00000590 03 00 01 00 00 00 20 2A B4 00 03 9A 00 1B B2 00 *? ? ? 000005A0 04 12 05 B6 00 06 2A BB 00 07 59 B7 00 08 B5 00 ? *? Y? ? 000005B0 09 2A 04 B5 00 03 B1 00 00 00 03 00 24 00 00 00 * ? ? $ 000005C0 16 00 05 00 00 00 17 00 07 00 18 00 0F 00 19 00 000005D0 1A 00 1A 00 1F 00 1C 00 25 00 00 00 0C 00 01 00 % 000005E0 00 00 20 00 26 00 27 00 00 00 29 00 00 00 03 00 & ' ) 000005F0 01 1F 00 01 00 2A 00 2B 00 02 00 23 00 00 00 6C * + # l 00000600 00 02 00 02 00 00 00 28 BB 00 0A 59 B7 00 0B 4C (? Y? L 00000610 2B 12 0C B6 00 0D 2B 10 0C B8 00 0E B6 00 0F 2B + ? + ? ? + 00000620 12 10 B6 00 11 2A B4 00 09 2B B9 00 12 02 00 B0 ? *? +? ? 00000630 00 00 00 02 00 24 00 00 00 16 00 05 00 00 00 1F $ 00000640 00 08 00 20 00 0E 00 21 00 17 00 22 00 1D 00 23 ! " # 00000650 00 25 00 00 00 16 00 02 00 00 00 28 00 26 00 27 % ( & ' 00000660 00 00 00 08 00 20 00 2C 00 2D 00 01 00 2E 00 00 , - . 00000670 00 04 00 01 00 2F 00 09 00 30 00 31 00 02 00 23 / 0 1 # 00000680 00 00 00 71 00 03 00 03 00 00 00 27 BB 00 13 59 q '? Y 00000690 B7 00 14 4C 2B B6 00 15 4D B2 00 04 BB 00 16 59 ? L+? M? ? Y 000006A0 B7 00 17 12 18 B6 00 19 2C B6 00 1A B6 00 1B B6 ? ? ,? ? ? 000006B0 00 06 B1 00 00 00 02 00 24 00 00 00 12 00 04 00 ? $ 000006C0 00 00 28 00 08 00 29 00 0D 00 2A 00 26 00 2B 00 ( ) * & + 000006D0 25 00 00 00 20 00 03 00 00 00 27 00 32 00 33 00 % ' 2 3 000006E0 00 00 08 00 1F 00 34 00 27 00 01 00 0D 00 1A 00 4 ' 000006F0 35 00 36 00 02 00 2E 00 00 00 04 00 01 00 2F 00 5 6 . / 00000700 01 00 37 00 00 00 02 00 38 7 8
0. class文件的整体架构格式如下:
type descriptor remark u4 magic 0xCAFEBABE u2 minor_version u2 major_version u2 constant_pool_count cp_info constant_pool[cosntant_pool_count – 1] index 0 is invalid u2 access_flags u2 this_class u2 super_class u2 interfaces_count u2 interfaces[interfaces_count] u2 fields_count field_info fields[fields_count] u2 methods_count method_info methods[methods_count] u2 attributes_count attribute_info attributes[attributes_count]
图表class文件整体架构格式如下:
1. 文件头
0xCAFE BABE: 前四个字节,魔数,代表文件类型,java的class文件固定为 0xCAFEBABE. 助记词: 咖啡宝贝
0x0000 0034: 接下来要知道版本号。版本号含主版本号和次版本号,都是各占2个字节。在此Demo种为0X0000 0034。其中前面的0000是次版本号,后面的0034是主版本号。通过进制转换得到的是次版本号为0,主版本号为52。
从oracle官方网站我们能够知道,52对应的正式jdk1.8,而其次版本为0,所以该文件的版本为1.8.0。如果需要验证,可以在用java –version命令输出版本号,或者修改编译目标版本–target重新编译,查看编译后的字节码文件版本号是否做了相应的修改。
2. 常量池
至此,我们共了解了前8字节的含义,下面讲讲常量池相关内容。
紧接着主版本号之后的就是常量池入口。常量池是Class文件中的资源仓库,在接下来的内容中我们会发现很多地方会涉及,如Class Name,Interfaces等。常量池中主要存储2大类常量:字面量和符号引用。字面量如文本字符串,java中声明为final的常量值等等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符。
为什么需要类和接口的全局限定名呢?系统引用类或者接口的时候不是通过内存地址进行操作吗?这里大家仔细想想,java虚拟机在没有将类加载到内存的时候根本都没有分配内存地址,也就不存在对内存的操作,所以java虚拟机首先需要将类加载到虚拟机中,那么这个过程设计对类的定位(需要加载A包下的B类,不能加载到别的包下面的别的类中),所以需要通过全局限定名来判别唯一性。这就是为什么叫做全局,限定的意思,也就是唯一性。
在进行具体常量池分析之前,我们先来了解一下常量池的项目类型表:
这里tag用来表示当前常量池不同类型的项。info中存放常量池项中存放的数据。
tag中表示的数据类型:
tag名称 remark tag值 CONSTANT_Utf8_info 记录字符串的值 1 CONSTANT_Integer_info 用于记录int类型的常量值 3 CONSTANT_Float_info 用于记录float类型的常量值 4 CONSTANT_Long_info 用于记录long类型的常量值 5 CONSTANT_Double_info 用于记录double类型的常量值 6 CONSTANT_Class_info 用于记录类或接口名 7 CONSTANT_String_info 用于记录常量字符串的值 8 CONSTANT_Fieldref_info 用于记录字段信息(包括类或接口中定义的字段以及代码中使用到的字段) 9 CONSTANT_Methodref_info 用于记录方法信息(包括类中定义的方法以及代码中使用到的方法) 10 CONSTANT_InterfaceMethodref_info 用于记录接口中的方法信息(包括接口中定义的方法以及代码中使用到的方法)11 CONSTANT_NameAndType_info 记录方法或字段的名称(name)和描述符(descriptor) 12 CONSTANT_MethodHandle_info 方法句柄表,1.7增加 15 CONSTANT_MethodType_info 方法类型表,1.7增加 16 CONSTANT_InvokeDynamic_info 动态方法调用点,1.7增加 18
上面的表中描述了14种数据类型的结构。接下来我们按照Demo的字节码进行逐一翻译。
0x006A: 由于常量池的数量不固定(n+2),所以需要在常量池的入口处放置一项u2类型的数据代表常量池数量。因此该6A进制是106,表示有105项常量,索引范围为1~105。Class文件格式规定,设计者就讲第0项保留出来了,以备后患。从这里我们知道接下来我们需要翻译出105项常量。
Constant #1 (一共有105个常量,这是第一个,以此类推…)
0x0A:从常量类型表中我们发现,第一个数据均是u1类型的tag,16进制的0a是十进制的10,对应表中的MethodRef_info。
0x001C: 指向方法描述符 CONSTANT_Class_info 的索引项, 为 #28, 查找得: #84 // java/lang/Object
0x0039: 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项, 为 #57, 查找得 #33:#34 // "<init>":()V
如上,依次计算出每个常量池的范围,直到 105项全部计算完成。
3 Access_Flag 访问标志
Flag Name Value Remarks ACC_PUBLIC 0x0001 pubilc,包外可访问。 ACC_FINAL 0x0010 final,不能有子类。 ACC_SUPER 0x0020 用于兼容早期编译器,新编译器都设置该标记,以在使用 invokespecial指令时对子类方法做特定处理。 ACC_INTERFACE 0x0200 接口,同时需要设置:ACC_ABSTRACT。不可同时设置:ACC_FINAL、ACC_SUPER、ACC_ENUM ACC_ABSTRACT 0x0400 抽象类,无法实例化。不可和ACC_FINAL同时设置。 ACC_SYNTHETIC 0x1000 synthetic,由编译器产生,不存在于源代码中。 ACC_ANNOTATION 0x2000 注解类型(annotation),需同时设置:ACC_INTERFACE、ACC_ABSTRACT ACC_ENUM 0x4000 枚举类型
访问标志信息包括该Class文件是类还是接口,是否被定义成public,是否是abstract,如果是类,是否被声明成final。通过上面的源代码,我们知道该文件是类并且是public。
4 this_class
0x0013: this_class是指向constant pool的索引值,该值必须是CONSTANT_Class_info类型,指定当前字节码定义的类或接口。
5 父类索引 super_class
0x001C: 同理:#28(Class #84 java/lang/Object)
6 接口索引 interfaces
0x0000: 通过java_byte.jpeg图我们知道,这个接口有2+n个字节,前两个字节表示的是接口数量,后面跟着就是接口的表。偏移量为: 4(magic)+4(version)+2+106*(constant)+2(access_flags)+2(this)+2(super)=122*,我们这个类没有任何接口,所以应该是0000。常量池是动态变化的,有点难算,不过算出来就是这个值。此处可以先找到super
7 字段表集合
字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。
同样,接下来就是2+n个字段属性。我们只有一个属性a,按道理应该是0001。查找文件果不其然是0001。
那么接下来我们要针对这样的字段进行解析。附上字段表如下:
type descriptor remark
u2 access_flags 记录字段的访问权限。
u2 name_index constant_pool中的索引,CONSTANT_Utf8_info类型。指定字段的名称。
u2 descriptor_index constant_pool中的索引,CONSTANT_Utf8_info类型,指定字段的描述符(见附录C)。
u2 attributes_count attributes包含的项目数。
attribute_info attributes[attributes_count]
0x00 02: 访问标志为private(自行搜索字段访问标志)
0x00 02: 字段名称索引为#2,对应的是 Methodref #19.#58 // com/youge/api/ByteCodeClassKen.init:()V
0x001D: 描述符索引为#29,对应的是 Utf8 userService
0x001E :描述符索引为#30,对应的是 Utf8 Lcom/youge/service/user/UserService;
0x00 00 :属性表数量为0,因此没有属性表。
8 方法
我们只有一个方法testMethod,按照道理应该前2个字节是0001。通过查找发现是0x00 02。这是什么原因,这代表着有2个方法呢?且继续看……
上图是一张方法表结构图,按照这个图我们分析下面的字节码:
type descriptor remark u2 access_flags 记录方法的访问权限。见2.9.1 u2 name_index constant_pool中的索引,CONSTANT_Utf8_info类型。指定方法名称。 u2 descriptor_index constant_pool中的索引,CONSTANT_Utf8_info类型,指定方法的描述符(见附录C)。 u2 attributes_count attributes包含的项目数。 attribute_info attributes[attributes_count] 字段中包含的Attribute集合。
第1个方法:
0x00 01: 访问标志 ACC_PUBLIC,表明该方法是public。(可自行搜索方法访问标志表)
0x00 07: 方法名索引为#7,对应的是"<init>"
0x00 08: 方法描述符索引为#8,对应的是"()V"
0x00 01: 属性表数量为1(一个属性表)
那么这里涉及到了属性表。什么是属性表呢?可以这么理解,它是为了描述一些专有信息的,上面的方法带有一张属性表。所有属性表的结构如下图:
一个u2的属性名称索引,一个u2的属性长度加上属性长度的info。
虚拟机规范预定义的属性有很多,比如Code,LineNumberTable,LocalVariableTable,SourceFile等等,这个网上可以搜索到。
按照上面的表结构解析得到下面信息:
Offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 00001344 00 23 00 00 00 3B 00 01 00 01 00 00 00 09 2A B7 # ; *? 00001360 00 01 2A B7 00 02 B1 00 00 00 02 00 24 00 00 00 *? ? $ 00001376 0E 00 03 00 00 00 12 00 04 00 13 00 08 00 14 00 00001392 25 00 00 00 0C 00 01 00 00 00 09 00 26 00 27 00 % & ' 00001408 00 00 02 00 28 00 22 00 01 00 23 00 00 00 63 00 ( " # c 00001424 03 00 01 00 00 00 20 2A B4 00 03 9A 00 1B B2 00 *? ? ? 00001440 04 12 05 B6 00 06 2A BB 00 07 59 B7 00 08 B5 00 ? *? Y? ? 00001456 09 2A 04 B5 00 03 B1 00 00 00 03 00 24 00 00 00 * ? ? $ 00001472 16 00 05 00 00 00 17 00 07 00 18 00 0F 00 19 00 00001488 1A 00 1A 00 1F 00 1C 00 25 00 00 00 0C 00 01 00 % 00001504 00 00 20 00 26 00 27 00 00 00 29 00 00 00 03 00 & ' ) 00001520 01 1F 00 01 00 2A 00 2B 00 02 00 23 00 00 00 6C * + # l 00001536 00 02 00 02 00 00 00 28 BB 00 0A 59 B7 00 0B 4C (? Y? L 00001552 2B 12 0C B6 00 0D 2B 10 0C B8 00 0E B6 00 0F 2B + ? + ? ? + 00001568 12 10 B6 00 11 2A B4 00 09 2B B9 00 12 02 00 B0 ? *? +? ?
0x0023:名称索引为#35("Code")。
0x0000 003B: 属性长度为59字节。
那么接下来解析一个Code属性表,按照下图解析 Code Attribute:
type descriptor remark u2 attribute_name_index constant_pool中的索引,CONSTANT_Utf8_info类型。指定Attribute的名称("Code")。 u4 attribute_length 该Attribute内容的字节长度。 u2 max_stack 该方法操作栈的最大深度。 u2 max_locals 该方法调用时需要分配的局部变量的最大个数,包括该方法的参数。 u4 code_length 该方法字节码长度(以字节为单位) u1 code[code_length] 存放字节码数组(字节码如何解析?)。 u2 exception_table_length 异常表的长度。 exception_table_info 每个表项记录一段异常处理代码信息和范围。 u2 start_pc 记录应用该项异常处理的起始字节码。在字节码数组中的起始索引号[start_pc, end_pc)。索引号必须是opcode(一条指令的开始位置)对应的位置。 u2 end_pc u2 handler_pc 记录该项异常处理代码的开始地址。在字节码数组中的开始索引号。索引号必须是opcode对应的位置。 u2 catch_type constant_pool中的索引,CONSTANT_Class_info类型。指定该项能捕获的异常类(或其子类)。或0用于实现finally语法(即不管什么类型都会捕获。) exception_table[exception_table_length] u2 attributes_count attributes包含的项目数。 attribute_info attributes[attributes_count]
前面6个字节(名称索引2字节+属性长度4字节)已经解析过了,所以接下来就是解析剩下的59-6=53字节即可。
0x00 01: max_stack=1
0x00 01: max_locals=1
0x0000 0009: code_length=9
0x2A B7 00 01 2A B7 00 02 B1: 这是code代码,可以通过虚拟机字节码指令进行查找。
2a=aload_0(将第一个引用变量推送到栈顶)
b7=invokespecial(调用父类构造方法)
00=什么都不做
01=将 #1 常量池推送到栈顶,即默认"<init>":()V 构造方法
2a=同上
b7=同上
00=什么都不做
02=将#2常量池推送栈顶,即 init() 方法
b1=return 从当前方法返回void
整理,去除无动作指令得到下面
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: invokespecial #2 // Method init:()V
8: return
接下来顺着Code属性表继续解析下去:
0x00 00 : exception_table_length=0
0x00 02 : attributes_count=2(Code属性表内部还含有2个属性表)
0x00 24: 第一个属性表是"LineNumberTable"
0x00 00 00 0E : "属性长度为14″
0x00 03 :line_number_table_length=3
line_number_table是一个数量为line_number_table_length,类型为line_number_info的集合,line_number_info表包括了start_pc和line_number两个u2类型的数据项,前者是字节码行号,后者是Java源码行号
0x00 00 : start_pc=0
0x00 12 : end_pc=18
0x00 04 : start_pc=4
0x00 13 : end_pc=19
0x00 25 第二个属性表是:"LocalVariableTable"
0x00 0000 0c:属性长度为12
0x00 01 : local_variable_table_length=1
然后按照local_variable_info表结构进行解析:
0x00 00 : start_pc=0
0x00 09:length=9
0x0026 : #38 name_index="this"
0x0027 : #39 descriptor_index #13 ("Lcom/youge/api/ByteCodeClassKen;")
0000 index=0
-----------到这里第一个方法就解析完成了--------------------
Method(<init>)–1个属性Code表-2个属性表(LineNumberTable ,LocalVariableTable)
10 Attribute
attributes数组记录了和类或接口相关的所有Attribute项(和字段相关的Attribute在field_info的attributes中,和方法相关的Attribute在method_info的attrubutes中,和字节码相关的Attribute在Code Attribute的attributes中)。attributes数组中的每项都是attribute_info类型,它描述了Attribute的名称、详细信息等。该attributes数组描述了ClassFile的一些额外信息。JVM必须忽略它不能识别的Attribute,而且那些JVM不能识别的的Attribute也不能影响class文件的语义。
当前定义的Attribute有:
Code Attribute、Constant Value Attibute、Deprecated Attribute、Enclosing Method Attribute、Exceptions Attribute、Inner Classes Attribute、Line Number Table Attribute、Local Variable Table Attribute、Local Variable Type Table Attribute、Runtime Visible Annotations Attribute、Runtime Invisible Annotation Attribute、Runtime Visible Parameter Annotation Attribute、Runtime Invisible Parameter Annotation Attribute、Signature Attribute、Source Debug Extension Attribute、Source File Attribute、Stack Map Table Attribute、Synthetic Attribute、Annotation Default Attribute等。
它们有些只存在于field_info中,有些只存在method_info中,有些只存在ClassFile中,有些只存在于Code Attribute中,还有些可以同时存在于field_info、method_info、classfile中。
Attribute结构只存在与ClassFile、method_info、field_info、Code Attribute结构中。
0x0002 :同样的,表示有2个Attributes了。
0x0037 : #15("SourceFile")
0x0000 0002 attribute_length=2
0x0038 : sourcefile_index = #56("ByteCodeClassKen.java")
SourceFile属性用来记录生成该Class文件的源码文件名称。
11. 直接使用 javap 反编译成可读的文本型字节码
javap -verbose xxx.class
Classfile /D:/www/test/target/classes/com/youge/api/ByteCodeClassKen.class Last modified 2018-9-26; size 1801 bytes MD5 checksum 76e207e502c3b096346480457d98cdef Compiled from "ByteCodeClassKen.java" public class com.youge.api.ByteCodeClassKen minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #28.#57 // java/lang/Object."<init>":()V #2 = Methodref #19.#58 // com/youge/api/ByteCodeClassKen.init:()V #3 = Fieldref #19.#59 // com/youge/api/ByteCodeClassKen.initialed:Z #4 = Fieldref #60.#61 // java/lang/System.out:Ljava/io/PrintStream; #5 = String #62 // init... #6 = Methodref #63.#64 // java/io/PrintStream.println:(Ljava/lang/String;)V #7 = Class #65 // com/youge/service/user/UserServiceImpl #8 = Methodref #7.#57 // com/youge/service/user/UserServiceImpl."<init>":()V #9 = Fieldref #19.#66 // com/youge/api/ByteCodeClassKen.userService:Lcom/youge/service/user/UserService; #10 = Class #67 // com/youge/pojo/user/UserInfo #11 = Methodref #10.#57 // com/youge/pojo/user/UserInfo."<init>":()V #12 = String #68 // lilei #13 = Methodref #10.#69 // com/youge/pojo/user/UserInfo.setName:(Ljava/lang/String;)V #14 = Methodref #70.#71 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #15 = Methodref #10.#72 // com/youge/pojo/user/UserInfo.setAge:(Ljava/lang/Integer;)V #16 = String #73 // new road. #17 = Methodref #10.#74 // com/youge/pojo/user/UserInfo.setAddress:(Ljava/lang/String;)V #18 = InterfaceMethodref #75.#76 // com/youge/service/user/UserService.addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer; #19 = Class #77 // com/youge/api/ByteCodeClassKen #20 = Methodref #19.#57 // com/youge/api/ByteCodeClassKen."<init>":()V #21 = Methodref #19.#78 // com/youge/api/ByteCodeClassKen.addUser:()Ljava/lang/Integer; #22 = Class #79 // java/lang/StringBuilder #23 = Methodref #22.#57 // java/lang/StringBuilder."<init>":()V #24 = String #80 // affect: #25 = Methodref #22.#81 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #26 = Methodref #22.#82 // java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; #27 = Methodref #22.#83 // java/lang/StringBuilder.toString:()Ljava/lang/String; #28 = Class #84 // java/lang/Object #29 = Utf8 userService #30 = Utf8 Lcom/youge/service/user/UserService; #31 = Utf8 initialed #32 = Utf8 Z #33 = Utf8 <init> #34 = Utf8 ()V #35 = Utf8 Code #36 = Utf8 LineNumberTable #37 = Utf8 LocalVariableTable #38 = Utf8 this #39 = Utf8 Lcom/youge/api/ByteCodeClassKen; #40 = Utf8 init #41 = Utf8 StackMapTable #42 = Utf8 addUser #43 = Utf8 ()Ljava/lang/Integer; #44 = Utf8 userInfo #45 = Utf8 Lcom/youge/pojo/user/UserInfo; #46 = Utf8 Exceptions #47 = Class #85 // java/lang/Exception #48 = Utf8 main #49 = Utf8 ([Ljava/lang/String;)V #50 = Utf8 args #51 = Utf8 [Ljava/lang/String; #52 = Utf8 classKen #53 = Utf8 affect #54 = Utf8 Ljava/lang/Integer; #55 = Utf8 SourceFile #56 = Utf8 ByteCodeClassKen.java #57 = NameAndType #33:#34 // "<init>":()V #58 = NameAndType #40:#34 // init:()V #59 = NameAndType #31:#32 // initialed:Z #60 = Class #86 // java/lang/System #61 = NameAndType #87:#88 // out:Ljava/io/PrintStream; #62 = Utf8 init... #63 = Class #89 // java/io/PrintStream #64 = NameAndType #90:#91 // println:(Ljava/lang/String;)V #65 = Utf8 com/youge/service/user/UserServiceImpl #66 = NameAndType #29:#30 // userService:Lcom/youge/service/user/UserService; #67 = Utf8 com/youge/pojo/user/UserInfo #68 = Utf8 lilei #69 = NameAndType #92:#91 // setName:(Ljava/lang/String;)V #70 = Class #93 // java/lang/Integer #71 = NameAndType #94:#95 // valueOf:(I)Ljava/lang/Integer; #72 = NameAndType #96:#97 // setAge:(Ljava/lang/Integer;)V #73 = Utf8 new road. #74 = NameAndType #98:#91 // setAddress:(Ljava/lang/String;)V #75 = Class #99 // com/youge/service/user/UserService #76 = NameAndType #42:#100 // addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer; #77 = Utf8 com/youge/api/ByteCodeClassKen #78 = NameAndType #42:#43 // addUser:()Ljava/lang/Integer; #79 = Utf8 java/lang/StringBuilder #80 = Utf8 affect: #81 = NameAndType #101:#102 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder; #82 = NameAndType #101:#103 // append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; #83 = NameAndType #104:#105 // toString:()Ljava/lang/String; #84 = Utf8 java/lang/Object #85 = Utf8 java/lang/Exception #86 = Utf8 java/lang/System #87 = Utf8 out #88 = Utf8 Ljava/io/PrintStream; #89 = Utf8 java/io/PrintStream #90 = Utf8 println #91 = Utf8 (Ljava/lang/String;)V #92 = Utf8 setName #93 = Utf8 java/lang/Integer #94 = Utf8 valueOf #95 = Utf8 (I)Ljava/lang/Integer; #96 = Utf8 setAge #97 = Utf8 (Ljava/lang/Integer;)V #98 = Utf8 setAddress #99 = Utf8 com/youge/service/user/UserService #100 = Utf8 (Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer; #101 = Utf8 append #102 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder; #103 = Utf8 (Ljava/lang/Object;)Ljava/lang/StringBuilder; #104 = Utf8 toString #105 = Utf8 ()Ljava/lang/String; { public com.youge.api.ByteCodeClassKen(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: invokespecial #2 // Method init:()V 8: return LineNumberTable: line 18: 0 line 19: 4 line 20: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lcom/youge/api/ByteCodeClassKen; public java.lang.Integer addUser() throws java.lang.Exception; descriptor: ()Ljava/lang/Integer; flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=1 0: new #10 // class com/youge/pojo/user/UserInfo 3: dup 4: invokespecial #11 // Method com/youge/pojo/user/UserInfo."<init>":()V 7: astore_1 8: aload_1 9: ldc #12 // String lilei 11: invokevirtual #13 // Method com/youge/pojo/user/UserInfo.setName:(Ljava/lang/String;)V 14: aload_1 15: bipush 12 17: invokestatic #14 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 20: invokevirtual #15 // Method com/youge/pojo/user/UserInfo.setAge:(Ljava/lang/Integer;)V 23: aload_1 24: ldc #16 // String new road. 26: invokevirtual #17 // Method com/youge/pojo/user/UserInfo.setAddress:(Ljava/lang/String;)V 29: aload_0 30: getfield #9 // Field userService:Lcom/youge/service/user/UserService; 33: aload_1 34: invokeinterface #18, 2 // InterfaceMethod com/youge/service/user/UserService.addUser:(Lcom/youge/pojo/user/UserInfo;)Ljava/lang/Integer; 39: areturn LineNumberTable: line 31: 0 line 32: 8 line 33: 14 line 34: 23 line 35: 29 LocalVariableTable: Start Length Slot Name Signature 0 40 0 this Lcom/youge/api/ByteCodeClassKen; 8 32 1 userInfo Lcom/youge/pojo/user/UserInfo; Exceptions: throws java.lang.Exception public static void main(java.lang.String[]) throws java.lang.Exception; descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=3, args_size=1 0: new #19 // class com/youge/api/ByteCodeClassKen 3: dup 4: invokespecial #20 // Method "<init>":()V 7: astore_1 8: aload_1 9: invokevirtual #21 // Method addUser:()Ljava/lang/Integer; 12: astore_2 13: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; 16: new #22 // class java/lang/StringBuilder 19: dup 20: invokespecial #23 // Method java/lang/StringBuilder."<init>":()V 23: ldc #24 // String affect: 25: invokevirtual #25 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: aload_2 29: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder; 32: invokevirtual #27 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 35: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 38: return LineNumberTable: line 40: 0 line 41: 8 line 42: 13 line 43: 38 LocalVariableTable: Start Length Slot Name Signature 0 39 0 args [Ljava/lang/String; 8 31 1 classKen Lcom/youge/api/ByteCodeClassKen; 13 26 2 affect Ljava/lang/Integer; Exceptions: throws java.lang.Exception } SourceFile: "ByteCodeClassKen.java"
字节码阅读或者简单反编译的字节码阅读能力,不是排查问题所必须的,但是,如果能够顺手读懂这些语言,无疑会给自己带来莫大的方便。另外,也能够更深层理解你写的代码本来的样子,从而从另一个角度发现不一样的自己。
后记,java字节码指令表速查:
### java字节码指令列表 字节码 助记符 指令含义 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 将do le型0推送至栈顶 0x0f dconst_1 将do le型1推送至栈顶 0x10 bipush 将单字节的常量值(-128~127)推送至栈顶 0x11 sipush 将一个短整型常量值(-32768~32767)推送至栈顶 0x12 ldc 将int, float或String型常量值从常量池中推送至栈顶 0x13 ldc_w 将int, float或String型常量值从常量池中推送至栈顶(宽索引) 0x14 ldc2_w 将long或do le型常量值从常量池中推送至栈顶(宽索引) 0x15 iload 将指定的int型本地变量 0x16 lload 将指定的long型本地变量 0x17 fload 将指定的float型本地变量 0x18 dload 将指定的do le型本地变量 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 将第一个do le型本地变量 0x27 dload_1 将第二个do le型本地变量 0x28 dload_2 将第三个do le型本地变量 0x29 dload_3 将第四个do le型本地变量 0x2a aload_0 将第一个引用类型本地变量 0x2b aload_1 将第二个引用类型本地变量 0x2c aload_2 将第三个引用类型本地变量 0x2d aload_3 将第四个引用类型本地变量 0x2e iaload 将int型数组指定索引的值推送至栈顶 0x2f laload 将long型数组指定索引的值推送至栈顶 0x30 faload 将float型数组指定索引的值推送至栈顶 0x31 daload 将do le型数组指定索引的值推送至栈顶 0x32 aaload 将引用型数组指定索引的值推送至栈顶 0x33 baload 将boolean或byte型数组指定索引的值推送至栈顶 0x34 caload 将char型数组指定索引的值推送至栈顶 0x35 saload 将short型数组指定索引的值推送至栈顶 0x36 istore 将栈顶int型数值存入指定本地变量 0x37 lstore 将栈顶long型数值存入指定本地变量 0x38 fstore 将栈顶float型数值存入指定本地变量 0x39 dstore 将栈顶do le型数值存入指定本地变量 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 将栈顶do le型数值存入第一个本地变量 0x48 dstore_1 将栈顶do le型数值存入第二个本地变量 0x49 dstore_2 将栈顶do le型数值存入第三个本地变量 0x4a dstore_3 将栈顶do le型数值存入第四个本地变量 0x4b astore_0 将栈顶引用型数值存入第一个本地变量 0x4c astore_1 将栈顶引用型数值存入第二个本地变量 0x4d astore_2 将栈顶引用型数值存入第三个本地变量 0x4e astore_3 将栈顶引用型数值存入第四个本地变量 0x4f iastore 将栈顶int型数值存入指定数组的指定索引位置 0x50 lastore 将栈顶long型数值存入指定数组的指定索引位置 0x51 fastore 将栈顶float型数值存入指定数组的指定索引位置 0x52 dastore 将栈顶do le型数值存入指定数组的指定索引位置 0x53 aastore 将栈顶引用型数值存入指定数组的指定索引位置 0x54 bastore 将栈顶boolean或byte型数值存入指定数组的指定索引位置 0x55 castore 将栈顶char型数值存入指定数组的指定索引位置 0x56 sastore 将栈顶short型数值存入指定数组的指定索引位置 0x57 pop 将栈顶数值弹出 (数值不能是long或do le类型的) 0x58 pop2 将栈顶的一个(long或do le类型的)或两个数值弹出(其它) 0x59 dup 复制栈顶数值并将复制值压入栈顶 0x5a dup_x1 复制栈顶数值并将两个复制值压入栈顶 0x5b dup_x2 复制栈顶数值并将三个(或两个)复制值压入栈顶 0x5c dup2 复制栈顶一个(long或do le类型的)或两个(其它)数值并将复制值压入栈顶 0x5d dup2_x1 dup_x1 指令的双倍版本 0x5e dup2_x2 dup_x2 指令的双倍版本 0x5f swap 将栈最顶端的两个数值互换(数值不能是long或do le类型的) 0x60 iadd 将栈顶两int型数值相加并将结果压入栈顶 0x61 ladd 将栈顶两long型数值相加并将结果压入栈顶 0x62 fadd 将栈顶两float型数值相加并将结果压入栈顶 0x63 dadd 将栈顶两do le型数值相加并将结果压入栈顶 0x64 is 将栈顶两int型数值相减并将结果压入栈顶 0x65 ls 将栈顶两long型数值相减并将结果压入栈顶 0x66 fs 将栈顶两float型数值相减并将结果压入栈顶 0x67 ds 将栈顶两do le型数值相减并将结果压入栈顶 0x68 imul 将栈顶两int型数值相乘并将结果压入栈顶 0x69 lmul 将栈顶两long型数值相乘并将结果压入栈顶 0x6a fmul 将栈顶两float型数值相乘并将结果压入栈顶 0x6b dmul 将栈顶两do le型数值相乘并将结果压入栈顶 0x6c idiv 将栈顶两int型数值相除并将结果压入栈顶 0x6d ldiv 将栈顶两long型数值相除并将结果压入栈顶 0x6e fdiv 将栈顶两float型数值相除并将结果压入栈顶 0x6f ddiv 将栈顶两do le型数值相除并将结果压入栈顶 0x70 irem 将栈顶两int型数值作取模运算并将结果压入栈顶 0x71 lrem 将栈顶两long型数值作取模运算并将结果压入栈顶 0x72 frem 将栈顶两float型数值作取模运算并将结果压入栈顶 0x73 drem 将栈顶两do le型数值作取模运算并将结果压入栈顶 0x74 ineg 将栈顶int型数值取负并将结果压入栈顶 0x75 lneg 将栈顶long型数值取负并将结果压入栈顶 0x76 fneg 将栈顶float型数值取负并将结果压入栈顶 0x77 dneg 将栈顶do le型数值取负并将结果压入栈顶 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型数值强制转换成do le型数值并将结果压入栈顶 0x88 l2i 将栈顶long型数值强制转换成int型数值并将结果压入栈顶 0x89 l2f 将栈顶long型数值强制转换成float型数值并将结果压入栈顶 0x8a l2d 将栈顶long型数值强制转换成do le型数值并将结果压入栈顶 0x8b f2i 将栈顶float型数值强制转换成int型数值并将结果压入栈顶 0x8c f2l 将栈顶float型数值强制转换成long型数值并将结果压入栈顶 0x8d f2d 将栈顶float型数值强制转换成do le型数值并将结果压入栈顶 0x8e d2i 将栈顶do le型数值强制转换成int型数值并将结果压入栈顶 0x8f d2l 将栈顶do le型数值强制转换成long型数值并将结果压入栈顶 0x90 d2f 将栈顶do le型数值强制转换成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 比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶 0x98 dcmpg 比较栈顶两do le型数值大小,并将结果(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 返回至本地变量 0xaa tableswitch 用于switch条件跳转,case值连续(可变长度指令) 0xab lookupswitch 用于switch条件跳转,case值不连续(可变长度指令) 0xac ireturn 从当前方法返回int 0xad lreturn 从当前方法返回long 0xae freturn 从当前方法返回float 0xaf dreturn 从当前方法返回do le 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下一条指令地址压入栈顶
分类助词符:
JVM指令助记符 变量到操作数栈:iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_ 操作数栈到变量:istore,istore_,lstore,lstore_,fstore,fstore_,dstore,dstor_,astore,astore_ 常数到操作数栈:bipush,sipush,ldc,ldc_w,ldc2_w,aconst_null,iconst_ml,iconst_,lconst_,fconst_,dconst_ 加:iadd,ladd,fadd,dadd 减:is ,ls ,fs ,ds 乘:imul,lmul,fmul,dmul 除:idiv,ldiv,fdiv,ddiv 余数:irem,lrem,frem,drem 取负:ineg,lneg,fneg,dneg 移位:ishl,lshr,iushr,lshl,lshr,lushr 按位或:ior,lor 按位与:iand,land 按位异或:ixor,lxor 类型转换:i2l,i2f,i2d,l2f,l2d,f2d(放宽数值转换) i2b,i2c,i2s,l2i,f2i,f2l,d2i,d2l,d2f(缩窄数值转换) 创建类实便:new 创建新数组:newarray,anewarray,multianwarray 访问类的域和类实例域:getfield,putfield,getstatic,putstatic 把数据装载到操作数栈:baload,caload,saload,iaload,laload,faload,daload,aaload 从操作数栈存存储到数组:bastore,castore,sastore,iastore,lastore,fastore,dastore,aastore 获取数组长度:arraylength 检相类实例或数组属性:instanceof,checkcast 操作数栈管理:pop,pop2,dup,dup2,dup_xl,dup2_xl,dup_x2,dup2_x2,swap 有条件转移:ifeq,iflt,ifle,ifne,ifgt,ifge,ifnull,ifnonnull,if_icmpeq,if_icmpene, if_icmplt,if_icmpgt,if_icmple,if_icmpge,if_acmpeq,if_acmpne,lcmp,fcmpl fcmpg,dcmpl,dcmpg 复合条件转移:tableswitch,lookupswitch 无条件转移:goto,goto_w,jsr,jsr_w,ret 调度对象的实便方法:invokevirt l 调用由接口实现的方法:invokeinterface 调用需要特殊处理的实例方法:invokespecial 调用命名类中的静态方法:invokestatic 方法返回:ireturn,lreturn,freturn,dreturn,areturn,return 异常:athrow finally关键字的实现使用:jsr,jsr_w,ret