通过案例class文件深入理解Java字节码结构

212 阅读39分钟

1 新建案例代码

  基础环境,jdk为jdk11

java version "11.0.14" 2022-01-18 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.14+8-LTS-263)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.14+8-LTS-263, mixed mode)

  编写案例类TestClass,一个基本的属性num,一个静态属性STAIC_NUM,另外连个个的方法add,一个add方法有参数,另外一个没有参数。


public class TestClass {
    private int num = 1;
    private int STATIC_NUM = 2;
    public int add(){
        return num + 1;
    }
    public int add(int addNum) {
        return num + addNum;
    }
}

  代码编译后生成class文件,反编译后,多了一个构造器,另外方法里面属性多了this引用,代码如下:

public class TestClass {
    private int num = 1;
    private int STATIC_NUM = 2;
    //编译器生成
    public TestClass() {
    }
    //多了this引用
    public int add() {
        return this.num + 1;
    }
    //多了this引用
    public int add(int addNum) {
        return this.num + addNum;
    }
}

2 查看字节码

  macos 查看class文件16进制,参考这里 image.png

3 字节码结构

  class文件是一组以8个字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在文件之中,中间没有添加任何分隔符。class文件格式有两种数据类型:无符号数和表。

  • 无符号数   无符号数属于基本数据类型,以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节。无符号数用来描述数字、索引引用、数量值、字符串值。
  • 表   表是由多个无符号数或者表座位数据项构成的复合数据类型,所有表的命名都习惯性以“_info”结尾。   Java字节码总的结构表如下所示,解析class文件信息的按照结构顺序逐一进行解读。
类型名称说明数量备注
u4magic魔数1识别文件类型,4个字节
u2minor_version副版本号12个字节
u2major_version主版本号12个字节
u2constant_pool_count常量池数量12个字节
cp_infoconstant_pool常量池信息constant_pool_countn个字节
u2access_flags访问标志12个字节
u2this_class类索引12个字节
u2super_class父类索引12个字节
u2interface_count接口数量12个字节
u2interfaces接口索引集合interface_countn个字节
u2fields_count字段数量12个字节
field_infofields字段信息fields_countn个字节
u2methods_count方法数量12个字节
method_infomethods方法信息集合methods_countn个字节
u2attributes_count附加属性计数器12个字节
attribute_infoattributes附加属性集合attributes_countn个字节

4 解析class文件

4.1 magic解析

类型名称说明数量备注
u4magic魔数1识别文件类型,4个字节

  解析magic,魔数作用是确定这个文件是否为一个能被虚拟机接收的class文件。魔数解析完为 0xCAFEBABE(咖啡宝贝???)。

image.png

4.2 minor_version 解析

类型名称说明数量备注
u4magic魔数1识别文件类型,4个字节
u2minor_version副版本号12个字节

  副版本号解析完0x0000。

image.png

4.3 major_version 解析

类型名称说明数量备注
u4magic魔数1识别文件类型,4个字节
u2minor_version副版本号12个字节
u2major_version主版本号12个字节

  主版本号解析完0x0037,3*16+7=55。

image.png

4.4 constant_pool_count 解析

类型名称说明数量备注
u4magic魔数1识别文件类型,4个字节
u2minor_version副版本号12个字节
u2major_version主版本号12个字节
u2constant_pool_count常量池数量12个字节

  常量池数量解析完0x001B,16+11=27。常量池计数从1开始,不是从0开始。27代表常量池数量为26。第0项常量空出来,目的后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义。

image.png

4.5 cp_info 解析

类型名称说明数量备注
u4magic魔数1识别文件类型,4个字节
u2minor_version副版本号12个字节
u2major_version主版本号12个字节
u2constant_pool_count常量池数量12个字节
cp_infoconstant_pool常量池信息constant_pool_countn个字节

  常量池中主要存放两大类常量:字面量和符号引用。字面量比较接近于java常量的概念,比如文本字符串、被声明为final的常量值等;符号引用则是主要是类和接口全限定名、字段的名称和描述符、方法的名称和描述符、方法句柄和方法类型、动态调用点和动态常量。

  常量池中每一项都是一个表,不同的JDK版本,常量表中分别有不同的常量类型。每个常量类型有tag标志,标志位都是u1,占用一个字节;然后每个常量类型有对应的结构体。

类型标志描述结构
CONSTANT_utf8_info1UTF-8编码字符串tag: u1 值为1
length: u2 bytes长度
bytes:u1 长度为length的utf-8字符串
CONSTANT_Integer_info3整形字面量tag: u1 值为3
bytes:u4 存储int值
CONSTANT_Float_info4浮点型字面量tag: u1 值为4
bytes:u4 存储float值
CONSTANT_Long_info5长整型字面量tag: u1 值为5
bytes:u8 存储long值
CONSTANT_Double_info6双精度浮点型字面量tag: u1 值为3
bytes:u8 存储double值
CONSTANT_Class_info7类或接口的符号引用tag: u1 值为7
index:u2 存储常量池索引号
CONSTANT_String_info8字符串类型字面量tag: u1 值为8
index:u2 存储常量池索引号
CONSTANT_Fieldref_info9字段的符号引用tag: u1 值为9
index:u2 CONSTANT_Class_info索引号
index:u2 CONSTANT_NameAndType_info 索引号
CONSTANT_Methodref_info10类中方法的符号引用tag: u1 值为10
index:u2 CONSTANT_Class_info索引号
index:u2 CONSTANT_NameAndType_info 索引号
CONSTANT_InterfaceMethodref_info11接口中方法的符号引用tag: u1 值为11
index:u2 CONSTANT_Class_info索引号
index:u2 CONSTANT_NameAndType_info 索引号
CONSTANT_NameAndType_info12字段或方法的符号引用tag: u1 值为12
index:u2 字段名或者方法名常量池索引号
index:u2 字段或者方法描述符常量池索引号
| CONSTANT_MethodHandle_info | 15 |表示方法句柄 |tag: u1 值为15<br/>reference_kind:值必须在1-9之间,决定了方法句柄的类型<br/>reference_index: u2 常量池索引|

| CONSTANT_MothodType_info | 16 | 标志方法类型|tag: u1 值为16
index:u2 常量池索引 | | CONSTANT_Dynamic_info | 17 |动态计算常量 |tag: u1 值为17
method_index:u2 索引
index:u2 常量池索引| | CONSTANT_InvokeDynamic_info | 18 |表示一个方法调用点 |tag: u1 值为18
method_index:u2 索引
index:u2 常量池索引| | CONSTANT_Module_info | 19 |表示一个模块 |tag: u1 值为19
index:u2 常量池索引| | CONSTANT_Package_info | 20 |表示一个模块中开放或者导出的包 |tag: u1 值为20
index:常量池索引|

  描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的void类型都用一个大写字符来表示,而对象类型则用字符L加对象的全限定名来表示。对于数组类型,每一维度将使用一个前置的[字符来描述,如一个定义为java.lang.String[][]类型的二维数组,将被记录为:[[Ljava/lang/String;,,一个整型数组int[]被记录为[I。用描述符来描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号“( )”之内。如方法java.lang.String toString()的描述符为( ) LJava/lang/String;,方法int abc(int[] x, int y)的描述符为([II) I。

  数据类型标志符定义如下:

标志符含义
B基本数据类型byte
C基本数据类型char
D基本数据类型double
F基本数据类型float
I基本数据类型int
J基本数据类型long
S基本数据类型short
Z基本数据类型boolean
V基本数据类型void
L对象类型,如Ljava/lang/Object

  java提供javap查看,命令javap -verbose TestClass.class,显示常量池结构

image.png

4.5.1 常量解析-1

  解析常量池tag=0x0A ,为10,查询常量池结构tag=10,为CONSTANT_Methodref_info。 image.png

CONSTANT_Methodref_info为类中方法的符号引用。tag: u1 值为10;index:u2 CONSTANT_Class_info索引号;index:u2 CONSTANT_NameAndType_info 索引号。tag后面是记录两个索引。一个是CONSTANT_Class_info索引号,u2;一个是CONSTANT_NameAndType_info 索引号,u2。 CONSTANT_Class_info索引号=0x0005(5),CONSTANT_NameAndType_info 索引号=0x0016(22)

image.png

  解析后为**#1 #5 #22** 对应如下

image.png

4.5.2 常量解析-2

  解析常量池tag=0x09,为9,查询常量池结构tag=9,为CONSTANT_Fieldref_info。

image.png

CONSTANT_Fieldref_info为字段的符号引用。tag: u1 值为9;index:u2 CONSTANT_Class_info索引号;index:u2 CONSTANT_NameAndType_info 索引号。tag后面是记录两个索引。一个是CONSTANT_Class_info索引号,u2;一个是CONSTANT_NameAndType_info 索引号,u2。 CONSTANT_Class_info索引号=0x0004(4),CONSTANT_NameAndType_info 索引号=0x0017(23)

image.png

  解析后为**#2 #4 #23** 对应如下

image.png

4.5.3 常量解析-3

  解析常量池tag=0x09,为9,查询常量池结构tag=9,为CONSTANT_Fieldref_info。

image.png CONSTANT_Fieldref_info为字段的符号引用。tag: u1 值为9;index:u2 CONSTANT_Class_info索引号;index:u2 CONSTANT_NameAndType_info 索引号。tag后面是记录两个索引。一个是CONSTANT_Class_info索引号,u2;一个是CONSTANT_NameAndType_info 索引号,u2。 CONSTANT_Class_info索引号=0x0004(4),CONSTANT_NameAndType_info 索引号=0x0018(24)

image.png

  解析后为**#3 #4 #24** 对应如下

image.png

4.5.4 常量解析-4

  解析常量池tag=0x07,为7,查询常量池结构tag=7,为CONSTANT_Class_info。

image.png

  CONSTANT_Class_info为类或接口的符号引用。tag: u1 值为7;index:u2 存储常量池索引号。

image.png

  解析后为**#4 #25** 对应如下

image.png

4.5.5 常量解析-5

  解析常量池tag=0x07,为7,查询常量池结构tag=7,为CONSTANT_Class_info。

image.png

  CONSTANT_Class_info为类或接口的符号引用。tag: u1 值为7;index:u2 存储常量池索引号。

image.png   解析后为**#5 #26** 对应如下

image.png

4.5.6 常量解析-6

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x0003代表字符串的长度,那再往后面取3个字节,0x6E756D代表对应字符串内容“num”。 解析后为**#6 num** 对应如下

image.png

4.5.7 常量解析-7

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x0001代表字符串的长度,那再往后面取1个字节,0x49代表对应字符串内容“I”。 解析后为**#7 I** 对应如下

image.png

4.5.8 常量解析-8

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x000A代表字符串的长度,那再往后面取10个字节,0x53544154 49435F4E 554D代表对应字符串内容“STATIC_NUM”。 解析后为**#8 STATIC_NUM** 对应如下

image.png

4.5.9 常量解析-9

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x0006代表字符串的长度,那再往后面取6个字节,0x3C696E6 9743E代表对应字符串内容“”。 解析后为**#9 ** 对应如下

image.png

4.5.10 常量解析-10

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x0003代表字符串的长度,那再往后面取3个字节,0x282956代表对应字符串内容“()V”。 解析后为**#10 ()V** 对应如下

image.png

4.5.11 常量解析-11

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x0004代表字符串的长度,那再往后面取4个字节,0x436F6465代表对应字符串内容“code”。 解析后为**#11 code** 对应如下

image.png

4.5.12 常量解析-12

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x000F代表字符串的长度,那再往后面取16个字节,0x4C696E65 4E756D62 65725461 626C65代表对应字符串内容“LineNumberTable”。 解析后为**#12 LineNumberTable** 对应如下

image.png

4.5.13 常量解析-13

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x0012代表字符串的长度,那再往后面取18个字节,0x4C6F6361 6C566172 6961626C 65546162 6C65代表对应字符串内容“LocalVariableTable”。 解析后为**#13 LocalVariableTable** 对应如下

image.png

4.5.14 常量解析-14

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为0x0004代表字符串的长度,那再往后面取4个字节,0x74686973代表对应字符串内容“this”。 解析后为**#14 this** 对应如下

image.png

4.5.15 常量解析-15

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为**#15 Lcom/youlangta/study/TestClass;** 对应如下

image.png

4.5.16 常量解析-16

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。 image.png

  解析后为**#16 add** 对应如下 image.png

4.5.17 常量解析-17

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png   CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为**#17 ()I** 对应如下

image.png

4.5.18 常量解析-18

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为**#18 (I)I** 对应如下

image.png

4.5.19 常量解析-19

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为**#19 addNum** 对应如下

image.png

4.5.20 常量解析-20

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为**#20 SourceFile** 对应如下

image.png

4.5.21 常量解析-21

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png   解析后为**#21 TestClass.java** 对应如下

image.png

4.5.22 常量解析-22

  解析常量池tag=0x0C,为12,查询常量池结构tag=12,为 CONSTANT_NameAndType_info。

image.png

CONSTANT_NameAndType_info为字段或方法的符号引用。tag: u1 值为12;index:u2 字段名或者方法名常量池索引号;index:u2 字段或者方法描述符常量池索引号。

image.png

  解析后为**#22 #9 #10** 对应如下

image.png

4.5.23 常量解析-23

  解析常量池tag=0x0C,为12,查询常量池结构tag=12,为 CONSTANT_NameAndType_info。

image.png CONSTANT_NameAndType_info为字段或方法的符号引用。tag: u1 值为12;index:u2 字段名或者方法名常量池索引号;index:u2 字段或者方法描述符常量池索引号。

image.png   解析后为**#23 #6 #7** 对应如下

image.png

4.5.24 常量解析-24

  解析常量池tag=0x0C,为12,查询常量池结构tag=12,为 CONSTANT_NameAndType_info。

image.png

CONSTANT_NameAndType_info为字段或方法的符号引用。tag: u1 值为12;index:u2 字段名或者方法名常量池索引号;index:u2 字段或者方法描述符常量池索引号。

image.png

  解析后为**#24 #8 #7** 对应如下

image.png

4.5.25 常量解析-25

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为**#25 com/youlangta/study/TestClass** 对应如下 image.png

4.5.26 常量解析-26

  解析常量池tag=0x01,为1,查询常量池结构tag=1,为 CONSTANT_utf8_info。

image.png

  CONSTANT_utf8_info为UTF-8编码字符串。tag: u1 值为1;length: u2 bytes长度;bytes:u1 长度为length的utf-8字符串。

image.png

  解析后为**#26 java/lang/Object** 对应如下 image.png

4.6 access_flags 访问标志解析

  访问标志标识了泪或接口的访问权限信息。各种访问标识如下所示:

标志名称标志值含义
ACC_PUBLIC0x0001是否为Public类型
ACC_FINAL0x0010是否被声明为final,只有类可以设置
ACC_SUPER0x0020是否允许使用invokespecial字节码指令的新语义,JDK1.0.2之后编译出来的类的这个标志默认为真
ACC_INTERFACE0x0200标志这是一个接口
ACC_ABSTRACT0x0400是否为abstract类型,对于接口或者抽象类来说,次标志值为真,其他类型为假
ACC_SYNTHETIC0x1000标志这个类并非由用户代码产生
ACC_ANNOTATION0x2000标志这是一个注解
ACC_ENUMx4000标志这是一个枚举

  解析值为0x0021,public类。

image.png

4.7 this_class 类索引解析

  类索引。0x0004,对应常量池#4。

image.png

4.8 super_class 父类索引解析

  父类索引。0x0005,对应常量池#5。

image.png

4.9 interface_count 接口数量解析

  接口数量。0x0000,说明没有实现接口。 image.png

4.10 interfaces 接口索引集合解析

  案例没有实现接口,索引这里不需要解析。

4.11 fields_count 字段数量解析

  解析类定义的字段数量,包括类级别变量以及实例变量。解析结果为2.

image.png

4.12 fields 字段信息解析

  字段信息解析即为字段表解析。字段表结构如下:

类型名称含义数量
u2access_flags访问标志1
u2name_index字段名索引1
u2descriptor_index描述符索引1
u2attributes_count属性计数器1
attribute_infoattributes属性集合attributes_count

  访问标志定义如下:

标志名称标志值含义
ACC_PUBLIC0x0001字段是否为public
ACC_PRIVATE0x0002字段是否为private
ACC_PROTECTED0x0004字段是否为protected
ACC_STATIC0x0008字段是否为static
ACC_FINAL0x0010字段是否为final
ACC_VOLATILE0x0040字段是否为volatile
ACC_TRANSTENT0x0080字段是否为transient
ACC_SYNCHETIC0x1000字段是否为由编译器自动产生
ACC_ENUM0x4000字段是否为enum

  第一个为 access_flags=0x0002,name_index=0x0006,descriptor_index=0x0007, attributes_count=0x0000,即为“private #6 #7”,对应如下

image.png

image.png

  第二个为 access_flags=0x0002,name_index=0x0008,descriptor_index=0x0007, attributes_count=0x0000,即为“private #8 #7”,对应如下

image.png

image.png

4.13 methods_count 方法数量解析

  方法数量解析,数量为3。其中一个是编译生成的方法。

image.png

image.png

4.14 methods 方法信息集合解析

  方法信息集合即为方法表解析。方法表结构和属性表结构一致,如下:

类型名称含义数量
u2access_flags访问标志1
u2name_index方法名索引1
u2descriptor_index描述符索引1
u2attributes_count属性计数器1
attribute_infoattributes属性集合attributes_count

  访问标志如下:

标志名称标志值含义
ACC_PUBLIC0x0001方法是否为public
ACC_PRIVATE0x0002方法是否为private
ACC_PROTECTED0x0004方法是否为protected
ACC_STATIC0x0008方法是否为static
ACC_FINAL0x0010方法是否为final
ACC_SYHCHRONRIZED0x0020方法是否为synchronized|
ACC_BRIDGE0x0040方法是否是有编译器产生的方法
ACC_VARARGS0x0080方法是否接受参数
ACC_NATIVE0x0100方法是否为native
ACC_ABSTRACT0x0400方法是否为abstract
ACC_STRICTFP0x0800方法是否为strictfp
ACC_SYNTHETIC0x1000方法是否是有编译器自动产生的|

  属性类型如下:

属性名称使用位置含义
Code方法表Java代码编译成的字节码指令
ConstantValue字段表final关键字定义的常量池
Deprecated类,方法,字段表被声明为deprecated的方法和字段
Exceptions方法表方法抛出的异常
EnclosingMethod类文件仅当一个类为局部类或者匿名类是才能拥有这个属性,这个属性用于标识这个类所在的外围方法
:-------------::----::----------------------------------------:
InnerClass类文件内部类列表
LineNumberTableCode属性Java源码的行号与字节码指令的对应关系
LocalVariableTableCode属性方法的局部变量描述
StackMapTableCode属性JDK1.6中新增的属性,供新的类型检查检验器检查和处理目标方法的局部变量和操作数有所需要的类是否匹配
Signature类,方法表,字段表用于支持泛型情况下的方法签名|
SourceFile类文件记录源文件名称
SourceDebugExtension类文件用于存储额外的调试信息
Synthetic类,方法表,字段表标志方法或字段为编译器自动生成的|
LocalVariableTypeTable使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加
RuntimeVisibleAnnotations类,方法表,字段表为动态注解提供支持
RuntimeInvisibleAnnotations表,方法表,字段表用于指明哪些注解是运行时不可见的
RuntimeVisibleParameterAnnotation方法表作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法
RuntimeInvisibleParameterAnnotation方法表作用与RuntimeInvisibleAnnotations属性类似,作用对象哪个为方法参数
AnnotationDefault方法表用于记录注解类元素的默认值
BootstrapMethods类文件用于保存invokeddynamic指令引用的引导方式限定符

  第一个方法解析,“public #9 #10 #11” ,对应"public ()V Code",结果为方法访问标志为public,方法名称为**,方法描述符为()V**,属性数量为1,对应为Code image.png

  这里涉及到属性解析,虚拟机规范定义的属性表如下所示:

属性名称使用位置含义
Code方法表Java代码编译成的字节码指令
ConstantValue字段表final关键字定义的常量池
Deprecated类,方法,字段表被声明为deprecated的方法和字段
Exceptions方法表方法抛出的异常
EnclosingMethod类文件仅当一个类为局部类或者匿名类是才能拥有这个属性,这个属性用于标识这个类所在的外围方法
InnerClass类文件内部类列表
LineNumberTableCode属性Java源码的行号与字节码指令的对应关系
LocalVariableTableCode属性方法的局部变量描述
StackMapTableCode属性JDK1.6中新增的属性,供新的类型检查检验器检查和处理目标方法的局部变量和操作数有所需要的类是否匹配
Signature类,方法表,字段表用于支持泛型情况下的方法签名
SourceFile类文件记录源文件名称
SourceDebugExtension类文件用于存储额外的调试信息
Synthetic类,方法表,字段表标志方法或字段为编译器自动生成的
LocalVariableTypeTable使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加
RuntimeVisibleAnnotations类,方法表,字段表为动态注解提供支持
RuntimeInvisibleAnnotations表,方法表,字段表用于指明哪些注解是运行时不可见的
RuntimeVisibleParameterAnnotation方法表作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法
RuntimeInvisibleParameterAnnotation方法表作用与RuntimeInvisibleAnnotations属性类似,作用对象哪个为方法参数
AnnotationDefault方法表用于记录注解类元素的默认值
BootstrapMethods类文件用于保存invokeddynamic指令引用的引导方式限定符

  对于每一个属性,它的名称都要从常量池中引用一个常量来表示,而属性值的结构则是完全自定义,只需要通过一个u4的长度属性去说明属性值内容所占用的位数。

类型名称数量
u2attribute_name_index1
u4attribute_length1
u1infoattribute_length
  • Code 属性   Java程序方法里面的代码经过Javac编译器处理之后,最终变为字节码指令存储在Code属性内。Code属性出现在方法表的属性集合中,但是并非所有的方法都必须存在这个属性,譬如接口或者抽象类中的方法就不存在Code属性。Code属性结构如下: 类型 | 名称 | 数量 | 含义 | | :-: | :------------------: | :-: | :--------: | | u2 | attribute_name_index | 1 | 属性名索引 | | u4 | attribute_length | 1 | 属性长度 | | u2 | max_stack | 1 | 操作数栈深度的最大值| |u2 | max_locals | 1 | 局部变量表所需的存续空间 | | u4 | code_length | 1 | 字节码指令的长度 | | u1 | code | code_length | 存储字节码指令 | | u2 | exception_table_length | 1 | 异常表长度| |exception_info | exception_table | exception_length | 异常表 | | u2 | attributes_count | 1 | 属性集合计数器 | | attribute_info | attributes | attributes_count | 属性集合|

  000B0000 00410002 00010000 000F2AB7 00012A04 B500022A 05B50003 B1000000 02000C00 00000E00 03000000 03000400 04000900 05000D00 00000C00 01000000 0F000E00 0F0000为方法的属性字节码信息。000B属性名称索引, 00000041代表属性长度 为65个字节,0002操作数栈深度的最大值为2,0001局部变量表存储需要的连续空间为1,0000000F字节码指令长度为15,2AB7 00012A04 B500022A 05B50003 B1为字节码指令,0000为异常表长度,0002属性集合计数器为2,000C为属性索引号,对应“#12 = Utf8 LineNumberTable”,00 00000E表示LineNumberTable字节码长度为14,00 03000000 03000400 04000900 05为LineNumberTable的字节码内容,000D为属性索引号,对应“#13 = Utf8 LocalVariableTable”,00 00000C为表示LineNumberTable字节码长度为12,00 01000000 0F000E00 0F0000为LocalVariableTable字节码内容 image.png

  对应一下内容: image.png

  LineNumberTable属性用于描述Java源码行号与字节码行号之间的对应关系。LineNumberTable结构如下所示:

类型名称数量含义
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u2line_number_table_length1行号表长度|
line_number_infoline_number_tableline_number_table_length行号表

  00 03000000 03000400 04000900 0500 03line_number_table_length的值为0x0003,即其行号表长度长度为3,即有三个行号表;line_number_info表包含start_pc和line_number两个u2数据项,前者是字节码行号,后者是Java源码行号,分别为000000 03即0对应3,000400 04即4对应4,000900 05即9对应5。

  LocalVariableTable 属性用于描述栈桢中局部变量表的变量与Java源码定义的变量之间的关系。这个不是运行时必须的属性,可以用-g:none或者-g:vars选项来取消或者要求生成这项信息。LocalVariableTable结构如下所示:

类型名称数量含义
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u2local_variable_table_length1局部变量关联信息长度|
u1local_variable_table1局部变量关联信息|
  局部变量关联信息结构如下:
类型名称数量含义
:-::----------------------::-::---:
u2start_pc1字节码偏移量
u2length1长度
u2name_index1名称索引号
u2descriptor_index1描述符
u2index1局部变量表变量槽位置

  start_pc和length代表了这个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖的长度,两者结合起来就是这个局部变量在字节码之中的作用域范围。name_index和descriptor_index指向常量池索引。index是这个局部变量表在变量槽位置。

  00 01000000 0F000E00 0F000000 01代表局部变量关联长度为1,0000start_pc为0,00 0Flength为15,000Ename_index为14,即“#14 = Utf8 this”,00 0F描述符为15,即“#15 = Utf8 Lcom/youlangta/study/TestClass;”,0000index为0。

  剩下的按照此方法一一解析,这里就不枚举了。

4.15 attributes_count 附加属性计数器解析

4.16 attributes 附加属性集合解析

5 字节码指令简介

字节码助记符指令含义
0x00nop什么都不做
0x01aconst_null将null推送至栈顶
0x02iconst_m1将int型-1推送至栈顶
0x03iconst_0将int型0推送至栈顶
0x04iconst_1将int型1推送至栈顶
0x05iconst_2将int型2推送至栈顶
0x06iconst_3将int型3推送至栈顶
0x07iconst_4将int型4推送至栈顶
0x08iconst_5将int型5推送至栈顶
0x09lconst_0将long型0推送至栈顶
0x0alconst_1将long型1推送至栈顶
0x0bfconst_0将float型0推送至栈顶
0x0cfconst_1将float型1推送至栈顶
0x0dfconst_2将float型2推送至栈顶
0x0edconst_0将do le型0推送至栈顶
0x0fdconst_1将do le型1推送至栈顶
0x10bipush将单字节的常量值(-128~127)推送至栈顶
0x11sipush将一个短整型常量值(-32768~32767)推送至栈顶
0x12ldc将int, float或String型常量值从常量池中推送至栈顶
0x13ldc_w将int, float或String型常量值从常量池中推送至栈顶(宽索引)
0x14ldc2_w将long或do le型常量值从常量池中推送至栈顶(宽索引)
0x15iload将指定的int型本地变量
0x16lload将指定的long型本地变量
0x17fload将指定的float型本地变量
0x18dload将指定的do le型本地变量
0x19aload将指定的引用类型本地变量
0x1aiload_0将第一个int型本地变量
0x1biload_1将第二个int型本地变量
0x1ciload_2将第三个int型本地变量
0x1diload_3将第四个int型本地变量
0x1elload_0将第一个long型本地变量
0x1flload_1将第二个long型本地变量
0x20lload_2将第三个long型本地变量
0x21lload_3将第四个long型本地变量
0x22fload_0将第一个float型本地变量
0x23fload_1将第二个float型本地变量
0x24fload_2将第三个float型本地变量
0x25fload_3将第四个float型本地变量
0x26dload_0将第一个do le型本地变量
0x27dload_1将第二个do le型本地变量
0x28dload_2将第三个do le型本地变量
0x29dload_3将第四个do le型本地变量
0x2aaload_0将第一个引用类型本地变量
0x2baload_1将第二个引用类型本地变量
0x2caload_2将第三个引用类型本地变量
0x2daload_3将第四个引用类型本地变量
0x2eiaload将int型数组指定索引的值推送至栈顶
0x2flaload将long型数组指定索引的值推送至栈顶
0x30faload将float型数组指定索引的值推送至栈顶
0x31daload将do le型数组指定索引的值推送至栈顶
0x32aaload将引用型数组指定索引的值推送至栈顶
0x33baload将boolean或byte型数组指定索引的值推送至栈顶
0x34caload将char型数组指定索引的值推送至栈顶
0x35saload将short型数组指定索引的值推送至栈顶
0x36istore将栈顶int型数值存入指定本地变量
0x37lstore将栈顶long型数值存入指定本地变量
0x38fstore将栈顶float型数值存入指定本地变量
0x39dstore将栈顶do le型数值存入指定本地变量
0x3aastore将栈顶引用型数值存入指定本地变量
0x3bistore_0将栈顶int型数值存入第一个本地变量
0x3cistore_1将栈顶int型数值存入第二个本地变量
0x3distore_2将栈顶int型数值存入第三个本地变量
0x3eistore_3将栈顶int型数值存入第四个本地变量
0x3flstore_0将栈顶long型数值存入第一个本地变量
0x40lstore_1将栈顶long型数值存入第二个本地变量
0x41lstore_2将栈顶long型数值存入第三个本地变量
0x42lstore_3将栈顶long型数值存入第四个本地变量
0x43fstore_0将栈顶float型数值存入第一个本地变量
0x44fstore_1将栈顶float型数值存入第二个本地变量
0x45fstore_2将栈顶float型数值存入第三个本地变量
0x46fstore_3将栈顶float型数值存入第四个本地变量
0x47dstore_0将栈顶do le型数值存入第一个本地变量
0x48dstore_1将栈顶do le型数值存入第二个本地变量
0x49dstore_2将栈顶do le型数值存入第三个本地变量
0x4adstore_3将栈顶do le型数值存入第四个本地变量
0x4bastore_0将栈顶引用型数值存入第一个本地变量
0x4castore_1将栈顶引用型数值存入第二个本地变量
0x4dastore_2将栈顶引用型数值存入第三个本地变量
0x4eastore_3将栈顶引用型数值存入第四个本地变量
0x4fiastore将栈顶int型数值存入指定数组的指定索引位置
0x50lastore将栈顶long型数值存入指定数组的指定索引位置
0x51fastore将栈顶float型数值存入指定数组的指定索引位置
0x52dastore将栈顶do le型数值存入指定数组的指定索引位置
0x53aastore将栈顶引用型数值存入指定数组的指定索引位置
0x54bastore将栈顶boolean或byte型数值存入指定数组的指定索引位置
0x55castore将栈顶char型数值存入指定数组的指定索引位置
0x56sastore将栈顶short型数值存入指定数组的指定索引位置
0x57pop将栈顶数值弹出 (数值不能是long或do le类型的)
0x58pop2将栈顶的一个(long或do le类型的)或两个数值弹出(其它)
0x59dup复制栈顶数值并将复制值压入栈顶
0x5adup_x1复制栈顶数值并将两个复制值压入栈顶
0x5bdup_x2复制栈顶数值并将三个(或两个)复制值压入栈顶
0x5cdup2复制栈顶一个(long或do le类型的)或两个(其它)数值并将复制值压入栈顶
0x5ddup2_x1dup_x1 指令的双倍版本
0x5edup2_x2dup_x2 指令的双倍版本
0x5fswap将栈最顶端的两个数值互换(数值不能是long或do le类型的)
0x60iadd将栈顶两int型数值相加并将结果压入栈顶
0x61ladd将栈顶两long型数值相加并将结果压入栈顶
0x62fadd将栈顶两float型数值相加并将结果压入栈顶
0x63dadd将栈顶两do le型数值相加并将结果压入栈顶
0x64is将栈顶两int型数值相减并将结果压入栈顶
0x65ls将栈顶两long型数值相减并将结果压入栈顶
0x66fs将栈顶两float型数值相减并将结果压入栈顶
0x67ds将栈顶两do le型数值相减并将结果压入栈顶
0x68imul将栈顶两int型数值相乘并将结果压入栈顶
0x69lmul将栈顶两long型数值相乘并将结果压入栈顶
0x6afmul将栈顶两float型数值相乘并将结果压入栈顶
0x6bdmul将栈顶两do le型数值相乘并将结果压入栈顶
0x6cidiv将栈顶两int型数值相除并将结果压入栈顶
0x6dldiv将栈顶两long型数值相除并将结果压入栈顶
0x6efdiv将栈顶两float型数值相除并将结果压入栈顶
0x6fddiv将栈顶两do le型数值相除并将结果压入栈顶
0x70irem将栈顶两int型数值作取模运算并将结果压入栈顶
0x71lrem将栈顶两long型数值作取模运算并将结果压入栈顶
0x72frem将栈顶两float型数值作取模运算并将结果压入栈顶
0x73drem将栈顶两do le型数值作取模运算并将结果压入栈顶
0x74ineg将栈顶int型数值取负并将结果压入栈顶
0x75lneg将栈顶long型数值取负并将结果压入栈顶
0x76fneg将栈顶float型数值取负并将结果压入栈顶
0x77dneg将栈顶do le型数值取负并将结果压入栈顶
0x78ishl将int型数值左移位指定位数并将结果压入栈顶
0x79lshl将long型数值左移位指定位数并将结果压入栈顶
0x7aishr将int型数值右(符号)移位指定位数并将结果压入栈顶
0x7blshr将long型数值右(符号)移位指定位数并将结果压入栈顶
0x7ciushr将int型数值右(无符号)移位指定位数并将结果压入栈顶
0x7dlushr将long型数值右(无符号)移位指定位数并将结果压入栈顶
0x7eiand将栈顶两int型数值作“按位与”并将结果压入栈顶
0x7fland将栈顶两long型数值作“按位与”并将结果压入栈顶
0x80ior将栈顶两int型数值作“按位或”并将结果压入栈顶
0x81lor将栈顶两long型数值作“按位或”并将结果压入栈顶
0x82ixor将栈顶两int型数值作“按位异或”并将结果压入栈顶
0x83lxor将栈顶两long型数值作“按位异或”并将结果压入栈顶
0x84iinc将指定int型变量增加指定值(i++, i–, i+=2)
0x85i2l将栈顶int型数值强制转换成long型数值并将结果压入栈顶
0x86i2f将栈顶int型数值强制转换成float型数值并将结果压入栈顶
0x87i2d将栈顶int型数值强制转换成do le型数值并将结果压入栈顶
0x88l2i将栈顶long型数值强制转换成int型数值并将结果压入栈顶
0x89l2f将栈顶long型数值强制转换成float型数值并将结果压入栈顶
0x8al2d将栈顶long型数值强制转换成do le型数值并将结果压入栈顶
0x8bf2i将栈顶float型数值强制转换成int型数值并将结果压入栈顶
0x8cf2l将栈顶float型数值强制转换成long型数值并将结果压入栈顶
0x8df2d将栈顶float型数值强制转换成do le型数值并将结果压入栈顶
0x8ed2i将栈顶do le型数值强制转换成int型数值并将结果压入栈顶
0x8fd2l将栈顶do le型数值强制转换成long型数值并将结果压入栈顶
0x90d2f将栈顶do le型数值强制转换成float型数值并将结果压入栈顶
0x91i2b将栈顶int型数值强制转换成byte型数值并将结果压入栈顶
0x92i2c将栈顶int型数值强制转换成char型数值并将结果压入栈顶
0x93i2s将栈顶int型数值强制转换成short型数值并将结果压入栈顶
0x94lcmp比较栈顶两long型数值大小,并将结果(1,0,-1)压入栈顶
0x95fcmpl比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x96fcmpg比较栈顶两float型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x97dcmpl比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将-1压入栈顶
0x98dcmpg比较栈顶两do le型数值大小,并将结果(1,0,-1)压入栈顶;当其中一个数值为NaN时,将1压入栈顶
0x99ifeq当栈顶int型数值等于0时跳转
0x9aifne当栈顶int型数值不等于0时跳转
0x9biflt当栈顶int型数值小于0时跳转
0x9cifge当栈顶int型数值大于等于0时跳转
0x9difgt当栈顶int型数值大于0时跳转
0x9eifle当栈顶int型数值小于等于0时跳转
0x9fif_icmpeq比较栈顶两int型数值大小,当结果等于0时跳转
0xa0if_icmpne比较栈顶两int型数值大小,当结果不等于0时跳转
0xa1if_icmplt比较栈顶两int型数值大小,当结果小于0时跳转
0xa2if_icmpge比较栈顶两int型数值大小,当结果大于等于0时跳转
0xa3if_icmpgt比较栈顶两int型数值大小,当结果大于0时跳转
0xa4if_icmple比较栈顶两int型数值大小,当结果小于等于0时跳转
0xa5if_acmpeq比较栈顶两引用型数值,当结果相等时跳转
0xa6if_acmpne比较栈顶两引用型数值,当结果不相等时跳转
0xa7goto无条件跳转
0xa8jsr跳转至指定16位offset位置,并将jsr下一条指令地址压入栈顶
0xa9ret返回至本地变量
0xaatableswitch用于switch条件跳转,case值连续(可变长度指令)
0xablookupswitch用于switch条件跳转,case值不连续(可变长度指令)
0xacireturn从当前方法返回int
0xadlreturn从当前方法返回long
0xaefreturn从当前方法返回float
0xafdreturn从当前方法返回do le
0xb0areturn从当前方法返回对象引用
0xb1return从当前方法返回void
0xb2getstatic获取指定类的静态域,并将其值压入栈顶
0xb3putstatic为指定的类的静态域赋值
0xb4getfield获取指定类的实例域,并将其值压入栈顶
0xb5putfield为指定的类的实例域赋值
0xb6invokevirtual调用实例方法
0xb7invokespecial调用超类构造方法,实例初始化方法,私有方法
0xb8invokestatic调用静态方法
0xb9invokeinterface调用接口方法
0xba无此指令
0xbbnew创建一个对象,并将其引用值压入栈顶
0xbcnewarray创建一个指定原始类型(如int, float, char…)的数组,并将其引用值压入栈顶
0xbdanewarray创建一个引用型(如类,接口,数组)的数组,并将其引用值压入栈顶
0xbearraylength获得数组的长度值并压入栈顶
0xbfathrow将栈顶的异常抛出
0xc0checkcast检验类型转换,检验未通过将抛出ClassCastException
0xc1instanceof检验对象是否是指定的类的实例,如果是将1压入栈顶,否则将0压入栈顶
0xc2monitorenter获得对象的锁,用于同步方法或同步块
0xc3monitorexit释放对象的锁,用于同步方法或同步块
0xc4wide<待补充>
0xc5multianewarray创建指定类型和指定维度的多维数组(执行该指令时,操作栈中必须包含各维度的长度值),并将其引用值压入栈顶
0xc6ifnull为null时跳转
0xc7ifnonnull不为null时跳转
0xc8goto_w无条件跳转(宽索引)
0xc9jsr_w跳转至指定32位offset位置,并将jsr_w下一条指令地址压入栈顶