内容导航
- 摘要
- 环境
- 实践
- class整体结构
- class解读征途(人工)
- 1、魔数
- 2、次版本号
- 3、主版本号
- 4、常量池计数
- 5、常量信息数组
- 6、访问标志
- 7、类索引
- 8、父类索引
- 9、直接接口计数器
- 10、接口信息表
- 11、字段计数器
- 12、字段信息表
- 13、方法计数器
- 14、方法信息表
- 14.1、方法1
- 14.2、方法2
- 14.2.1、访问标志(access_flags)
- 14.2.2、方法名索引值(name_index)
- 14.2.3、描述索引(descriptor_index)
- 14.2.4、其他属性的数量(attributes_count)
- 14.2.5、其他属性信息表
- 14.2.5.1、其它属性信息1
- 14.2.5.1.1、属性名索引值
- 14.2.5.1.2、属性长度
- 14.2.5.1.3、 操作数堆栈的最大深度
- 14.2.5.1.4、局部变量的数量
- 14.2.5.1.5、指令数组长度
- 14.2.5.1.6、指令信息表
- 14.2.5.1.6.1、指令信息1
- 14.2.5.1.7、异常表长度
- 14.2.5.1.7、异常表
- 14.2.5.1.8、Code的属性数量
- 14.2.5.1.9、Code的属性信息
- 14.2.5.1.9.1、属性1
- 14.2.5.1.9.1.1、属性名索引
- 14.2.5.1.9.1.2、属性长度
- 14.2.5.1.9.1.3、行数表长度
- 14.2.5.1.9.1.3、行数表
- 14.2.5.1.9.1.3.1、行数据1
- 14.2.5.1.9.1.3.2、行数据2
- 15、属性计数器
- 16、属性信息表
摘要
您好! 本篇旨在借助Hello World小程序讲解class文件结构,希望给您在您理解class结构的路上推波助澜。如有不足,请不吝赐教。
环境
Java:JDK8
JVM Doc:Java SE 8
class查看器:winhex
实践
源代码
喜闻乐见Hello World
,源码如下:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
对应class文件
javac编译HelloWorld.java生成HelloWorld.class。其过程就不废话
class文件查看
1、用上面列出的工具winhex打开HelloWorld.class,如下:
说明,图片是以1字节为基本单位的十六进制表示的class内容,class实际还是二进制存储的,只不过为了容易查看,将二进制转为十六进制而已。
2、使用如下javap命令
查看HelloClass.class结构:
显示内容如下:
此为虚拟机解读出来的结果。我们要做的就是模拟虚拟机解读过程。
class整体结构
参照官方虚拟机规范文档可知,class文件伪结构如下:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
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];
}
不一一说明,直接看下图。再有不明白,就看官方虚拟机规范文档.
图片说明:
方格中有文字的都是所占字节长度不确定,无文字的都是占1字节(魔数也在其列,方格中的只是作为固定值展示而已)。
class解读征途(人工)
记住一点,class文件是一串严格有序
、无分隔符
、无对齐符
的二进制流
。
1、魔数
长度:4字节
值:0xCAFEBABE
对应位置:
说明:固定值,为了表示此文件为一个class文件。
2、次版本号
长度:2字节
值:0x0000
对应位置:
对应javap展示结果:
3、主版本号
长度:2字节
值:0x0034,对应十进制52
对应位置:
说明:与上面次版本号组合标志class文件的版本。如主版本号为M,次版本号为m,则class文件版本为 M.m。
对应javap展示结果:
4、常量池计数
长度:2字节
值:0x001D
对应位置:
说明:十六进制换算成十进制为29,对应数组索引为,0 ~ 28,但索引0位置是虚拟机预留的,不可使用。故索引范围实际为1 ~ 28,表示容量池中仅有28个常量元素。
5、常量信息数组
常量是一个包含多个无符号数和项的复合数据结构。常量的通用伪结构模板如下:
cp_info {
u1 tag;
u1 info[];
}
因结构中未指定info[]长度,所以不能直接确定一个常量的具体长度。唯一可确定的是常量都是以一个字节的tag常量类型开始,所以只能先确定常量类型,再确定具体的伪结构。
5.1、常量1
5.1.1、常量类型
长度:1字节
值:0x0A
对应位置:
说明:十六进制转为十进制,对应值为10;参照常量类型表
可知,此常量类型为CONSTANT_Methodref
,对应的实际伪结构如下:
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
所以常量1对应数据位置:
三个红色小框分别对应伪结构的三项:tag、class_index、name_and_type_index。
项目 | 十六进制值 | 十进制值 | 解析值 |
---|---|---|---|
tag | 0x0A | 10 | CONSTANT_Methodref |
class_index | 0x0006 | 6 | |
name_and_type_index | 0x000F | 15 |
对应javap结果:
5.2、常量28
因篇幅问题,省略其它常量的分析过程,直接最后一个常量。
说明:由于常量类型0x01,查常量类型表可知该常量类型为:CONSTANT_Utf8,用来表示常量字符串值。而其对应伪结构如下:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
关于tag上面已经说过;length,占2字节,对应下面字节数组包含的元素数量。u1 bytes[length],表示字节数组,也即,下面是连续length个各占一字节的无符号数,最终应该是个字符串。
项目 | 十六进制值 | 十进制值 | 解析值 |
---|---|---|---|
tag | 0x01 | 1 | CONSTANT_Utf8 |
length | 0x0006 | 15 | |
bytes[length] | 0x28、0x4C、0x6A、0x61、0x76、0x61、0x2F、0x6C、0x61、0x6E、0x67、0x2F、0x53、0x74、0x72、0x69、0x6E、0x67、0x3B、0x29、0x56 | (Ljava/lang/String;)V |
对应位置:
说明:关于字节数组bytes[]转字符串,可以查询ASCII对照表,可以自己试一试,这里不累赘。
到此,常量池中的内容分析结束,让我们查看一下常量池全内容:
6、访问标志
长度:2字节
值:0x0021
对应位置:
说明:
0x0021转为二进制为:
对照上图可知,分别对应ACC_SUPER、ACC_PUBLIC
对应javap展示结果:
7、类索引
长度:2字节
值:0x0005
对应位置:
说明:此值为常量池索引值,此索引对应的常量的类型必须为CONSTANT_Class
对应javap展示结果:
8、父类索引
长度:2字节
值:0x0006
对应位置:
对应javap展示结果:
说明:此值为常量池索引值,非0索引对应的常量的类型必须为CONSTANT_Class,索引0则表示父类为Object。源码中HelloWorld并没有显式继承父类,这里确出现父类java.lang.Object。正好应证了java.lang.Object是所有类(除去Object)的父类。
9、直接接口计数器
长度:2字节
值:0x0000
对应位置:
说明:十进制数为0,表示没有显式实现接口。官方对此项的说明为:
The value of the interfaces_count item gives the number of direct superinterfaces of this class or interface type.
我理解为:该类或接口类型显式实现或继承的接口数量。
10、接口信息表
因为接口计数器指定值为0,索引接口表为空
11、字段计数器
长度:2字节
值:0x0000
对应位置:
说明:十进制为0,表示没有字段
12、字段信息表
因字段计数器指定值为0,此部分为空
13、方法计数器
长度:2字节
值:0x0002
对应位置:
说明:十进制值为2,有2个方法。
14、方法信息表
方法的伪结构如下:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
14.1、方法1
14.1.1、访问标志(access_flags)
长度:2字节
值:0x0001
对应位置:
说明:指定十进制值为1,查方法访问标志表
可知,对应ACC_PUBLIC
对应javap展示结果:
14.1.2、方法名索引值(name_index)
长度:2
值:0x0007
对应位置:
说明:对应常量池索引值7所存的常量,该常量类型必须为CONSTANT_Utf8
对应javap展示结果:
14.1.3、描述索引(descriptor_index)
长度:2字节
值:0x0008
对应位置:
说明:对应常量池索引值8所存的常量,该常量类型必须为CONSTANT_Utf8
对应javap展示结果:
14.1.4、其他属性的数量(attributes_count)
长度:2字节
值:0x0001
对应位置:
说明:此值表示该方法还有一个属性说明
14.1.5、其他属性信息表
其它属性的伪结构如下:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info [attribute_length];
}
14.1.5.1、其它属性信息1
14.1.5.1.1、属性名索引值
长度:2字节
值:0x0009
对应位置:
说明:对应常量池索引9对应的常量,该常量类型必须为CONSTANT_Utf8
对应javap展示结果:
14.1.5.1.2、属性长度
长度:4字节
值:0x0000001D
对应位置:
14.1.5.1.3、 属性表
根据上面属性长度所指示的值为29,表示接下来29个连续的各占1字节的均为属性信息。
长度:29字节
值:0x00、0x01、0x00、0x01、0x00、0x00、0x00、0x05、0x2A、0xB7、0x00、0x01、0xB1、0x00、0x00、0x00、0x01、0x00、0x0A、0x00、0x00、0x00、0x06、0x00、0x01、0x00、0x00、0x00、0x01
对应位置:
说明:根据属性名code,查询官网文档可知,该code实际伪结构如下:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
则此部分内容应该对应伪结构中从u2 max_stack
开始的所有项。具体我会在对第二个方法解读时候详解。
14.2、方法2
14.2.1、访问标志(access_flags)
长度:2字节
值:0x0009
对应位置:
说明:指定十进制值为9,查方法访问标志表
可知,对应ACC_PUBLIC、ACC_STATIC
对应javap展示结果:
14.2.2、方法名索引值(name_index)
长度:2
值:0x0007
对应位置:
说明:对应常量池索引值11所存的常量,该常量类型必须为CONSTANT_Utf8
对应javap展示结果:
14.2.3、描述索引(descriptor_index)
长度:2字节
值:0x0008
对应位置:
说明:对应常量池索引值12所存的常量,该常量类型必须为CONSTANT_Utf8
对应javap展示结果:
14.2.4、其他属性的数量(attributes_count)
长度:2字节
值:0x0001
对应位置:
说明:此值表示该方法还有一个属性说明
14.2.5、其他属性信息表
其它属性的通用伪结构如下:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info [attribute_length];
}
14.2.5.1、其它属性信息1
14.2.5.1.1、属性名索引值
长度:2字节
值:0x0009
对应位置:
说明:对应常量池索引9对应的常量,该常量类型必须为CONSTANT_Utf8
对应javap展示结果:
则该属性实际伪结构如下:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack;
u2 max_locals;
u4 code_length;
u1 code[code_length];
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
与方法1一致,这里就直接按实际伪结构一一解读。
14.2.5.1.2、属性长度
长度:4字节
值:0x00000025
对应位置:
14.2.5.1.3、 操作数堆栈的最大深度
长度:2字节
值:0x0002
对应位置:
对应javap展示结果:
14.2.5.1.4、局部变量的数量
长度:2字节
值:0x0001
对应位置:
说明:包括用于在调用该方法时将参数传递给该方法的局部变量。对于long或double类型值的最大局部变量索引是 max_locals - 2。任何其他类型的值的最大局部变量索引为max_locals - 1。
对应javap展示结果:
14.2.5.1.5、指令数组长度
长度:4字节
值:0x00000009
对应位置:
14.2.5.1.6、指令信息表
一个字节代表一条指令,可参照官方指令说明
14.2.5.1.6.1、指令信息1
长度:1字节
值:0xB2
对应位置:
查询官方指令对照表可知,表示指令 getstatic
对应javap展示结果:
余下指令查询结果如下:
字节码 | 指令 |
---|---|
00 | nop |
02 | iconst_m1 |
12 | ldc |
03 | iconst_0 |
B6 | invokevirtual |
00 | nop |
04 | iconst_1 |
B1 | return |
14.2.5.1.7、异常表长度
长度:2字节
值:0x0000
对应位置:
14.2.5.1.7、异常表
由上面属性可知为空,此项无
14.2.5.1.8、Code的属性数量
长度:2字节
值:0x0001
对应位置:
14.2.5.1.9、Code的属性信息
通用为伪结构如下:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
14.2.5.1.9.1、属性1
14.2.5.1.9.1.1、属性名索引
长度:2字节
值:0x000A
对应位置:
对应javap展示结果:
查询官网可知,实际对应伪结构为:
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
14.2.5.1.9.1.2、属性长度
长度:4字节
值:0x0000000A
对应位置:
14.2.5.1.9.1.3、行数表长度
长度:2字节
值:0x0002
对应位置:
14.2.5.1.9.1.3、行数表
之前伪结构中已经给出为:
{ u2 start_pc;
u2 line_number;
}
14.2.5.1.9.1.3.1、行数据1
项目 | 开始位置 | 行号 |
---|---|---|
对应值 | 0x0000 | 0x0003 |
十进制 | 0 | 3 |
对应位置:
对应javap展示结果:
14.2.5.1.9.1.3.2、行数据2
项目 | 开始位置 | 行号 |
---|---|---|
对应值 | 0x0008 | 0x0004 |
十进制 | 8 | 4 |
对应位置:
对应java展示结果位置:
15、属性计数器
长度:2字节
值:0x0001
对应位置:
16、属性信息表
伪结构如下:
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
16.1、属性信息1
16.1.1、属性名索引
长度:2字节
值:0x000D
对应位置:
对应javap展示结果:
则该属性实际的伪结构为:
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
16.1.2、属性长度
长度:4字节
值:0x00000002
对应位置:
16.1.3、源文件索引
长度:2字节
值:0x000E
对应位置:
对应javap展示结果: