类文件结构
类文件是一组以8位字节为基础的二进制流,各个数据必须严格按照顺序排列在类文件中,并且只有两种数据类型
- 无符号数: 描述数字、索引引用、数量值或者UTF-8编码的字符串
- 表: 由无符号数或其它表作为数据结构的符合数据类型
名称 | 类型 | 数量 |
---|---|---|
magic | u4 | 1 |
minor_version | u2 | 1 |
major_version | u2 | 1 |
constant_pool_count | u2 | 1 |
constant_pool | cp_info | constant_pool_count-1 |
access_flag | u2 | 1 |
this_class | u2 | 1 |
super_class | u2 | 1 |
interfaces_count | u2 | 1 |
interfaces | u2 | 1 |
fields_count | u2 | 1 |
field_info | fields | fields_count |
methods_count | u2 | 1 |
methods | methods_info | methods_count |
atrributes_count | u2 | 1 |
atrributes | atrribute_info | atrributes_count |
魔数和class文件版本
每个class文件头4个字节称为魔数(magic),用于确定这个class文件是否能被虚拟机接受,class文件魔数为0xCAFEBABE。
紧接着魔数后4个字节是class文件版本号,第5和第6字节是次版本号(minor_version),第7和第8字节是主版本号(major_version)
常量池
主版本号之后是常量池入口,常量池是class文件结构中与与其它数据关联最多的数据类型。常量池入口有一个u2类型的数据,代表常量池容量计数(constant_pool_count),这个值从1开始,0有特殊作用,表示在特定情况下不引用任何一个常量池项目。
常量池中主要存放一下两种常量
- 字面量(Literal): 常量,如文本字符串和被声明为final的常量值等
- 符号引用(Symbolic Reference):
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
访问标记
常量池之后是访问标记(access_flag),用于识别一些类或接口的访问信息
标记名称 | 标记值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public |
ACC_FINAL | 0x0010 | 是否为final,只有类可以设置 |
ACC_SUPER | 0x0020 | 是否允许使用invokespecial字节码指令,JDK 1.2之后为true |
ACC_INTERFACE | 0x0200 | 是否为接口 |
ACC_ABSTRACT | 0x0400 | 是否为abstract类型,接口和抽象类为true,其它为false |
ACC_SYNTHETIC | 0x1000 | 非用户代码产生 |
ACC_ANNOTATION | 0x2000 | 是否为注解 |
ACC_ENUM | 0x4000 | 是否为枚举 |
类索引、父类索引和接口索引集合
- 类索引(this_class): 用于确定这个类的全限定名
- 父类索引(super_class)是u2类型: 用于确定这个父类的全限定名
- 接口索引(interfaces_count): 用来描述这个类实现了哪些接口,是一组u2类型的数据集合
字段表集合
- 字段表(field_info): 用于描述接口或类中声明的变量
- 字段(fields): 包含了类级别变量或实例变量,但不包含方法内部声明的变量
字段表结构
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
access_flags | u2 | 1 | 访问限定标记 |
name_index | u2 | 1 | 对常量池引用,字段的简单名字 |
descriptor_index | u2 | 1 | 对常量池引用,字段和方法的描述符 |
attributes_count | u2 | 1 | |
attributes | attributes_info | 1 |
字段访问标志
|ACC_PUBLIC| 0x0001 | 是否为public|
|ACC_PRIVATE| 0x0002 | 是否为private|
|ACC_PROTECTED| 0x0004 | 是否为protected|
|ACC_STATIC| 0x0008| 是否为static|
|ACC_FINAL| 0x0010| 是否为final|
|ACC_VOLATILE| 0x0040| 是否为volitale|
|ACC_TRANSIENT| 0x0080| 是否为transient|
|ACC_SYNTHETIC| 0x1000| 是否由编译器自动产生|
|ACC_ENUM| 0x4000| 是否为枚举|
方法表集合
和字段表集合类似
属性表集合
属性表(attributes_info)用于描述某些场景专有的信息。
名称 | 使用位置 | 含义 |
---|---|---|
Code | 方法表 | Java代码变异成的字节码指令 |
ConstantValue | 字段表 | final关键定义的常量 |
Deprecated | 类、方法和字段表 | 被声明为deprecated的方法和字段 |
Execeptions | 方法表 | 方法跑出的异常 |
InnerClass | 类方法 | 内部类列表 |
LineNumberTable | Code属性 | Java源码的行号与字节指令的对应关系 |
LocalVariableTable | Code属性 | 方法的局部变量描述 |
SourceFile | 类文件 | 源文件名称 |
Synthetic | 类、方法和字段表 | 表示方法或字段为编译器自动生成 |
code属性
Java方法体里的代码被编译器处理后最终变成字节码存在code属性内
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | 指向CONSTANT_Utf8_info常量的索引,固定为Code |
attribute_length | u4 | 1 | 属性值的长度 |
max_stack | u2 | 1 | 操作栈最大深度,虚拟机运行时需要根据这个值来分配栈帧的栈深度 |
max_locals | u2 | 1 | 表示局部变量所需的储存空间,单位是slot,是虚拟机为局部变量分配内存所使用的最小单位(1 slot = 32bit) |
code_length | u4 | 1 | 字节码的长度,虽然是u4,但是字节码长度不能超过65535 |
code | u1 | code_length | 储存字节码 |
exception_table_length | u2 | 1 | |
exception_table | exception_info | 1 | |
attribues_count | u2 | 1 | |
attributes | attribute_info | attribues_count |
Exception属性
用于列举出方法中可能抛出的受查异常
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | |
attribute_length | u4 | 1 | 属性值的长度 |
number_of_exceptions | u2 | 1 | |
exception_index_table | u2 | number_of_exceptions |
LineNumberTable属性
用于描述Java源代码和字节码行号(偏移量)之间的对应关系,不是运行时必须的属性,如果不生成该属性就无法再抛出异常时返回代码中对应的行号。
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | 属性值的索引 |
attribute_length | u4 | 1 | 属性值的长度 |
line_number_table_Length | u2 | 1 | |
line_number_table | line_number_info | line_number_table_length |
LocalVariableTable属性
用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,不是运行时必须的属性,如果没有生成所有参数名称都将丢失
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | 属性值的索引 |
attribute_length | u4 | 1 | 属性值的长度 |
local_variable_table_Length | u2 | 1 | |
local_variable_table | local\ _variable_info | local_variable_table_length |
local_variable_info结构
名称 | 类型 | 数量 | 说明 | |
---|---|---|---|---|
start_pc | u2 | 1 | 这个局部变量声明周期开始时的字节码偏移量 | |
length | u2 | 1 | 这个局部变量声明周期开始时的字节码作用范围的长度 | |
name_index | u2 | 1 | 指向CONSTANT_Utf8_info常量的索引,代表局部变量的名称 | |
descriptor_index | 1 | 指向CONSTANT_Utf8_info常量的索引,代表局部变量的描述符 | ||
index | 1 | u2 | 表示这个局部变量在栈帧局部变量中slot的位置 |
SourceFile属性
用于记录生成这个class文件源代码的名称,是可选的,如果不生成这个属性,抛出异常时不会显示出错代码的文件名。
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | 属性值的索引 |
attribute_length | u4 | 1 | 属性值的长度 |
sourcefule_index | u2 | 1 | 指向CONSTANT_Utf8_info常量的索引,常量值时源码文件的文件名 |
ConstantValue属性
ConstantValue属性的作用是通知虚拟机自动为静态变量赋值,只有被static修饰的变量才能用这个属性
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | 属性值的索引 |
attribute_length | u4 | 1 | 属性值的长度 |
constant_value_index | u2 | 1 | 指向常量池的引用 |
InnerClasses属性
InnerClasses属性用于记录内部类与宿主类之间的关联,如果一个类中有内部类,则会生成InnerClass属性。
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | 属性值的索引 |
attribute_length | u4 | 1 | 属性值的长度 |
nubmber_of_classes | u2 | 1 | 代表记录多少个内部类信息 |
inner_class | inner_class_info | nubmber_of_classes |
inner_class_info
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
inner_class_info_index | u2 | 1 | 指向CONSTANT_Class_info常量的索引,代表内部类的符号引用 |
outter_class_info_index | u2 | 1 | 指向CONSTANT_Class_info常量的索引,代表宿主类的符号引用 |
inner_name_index | u2 | 1 | 指向CONSTANT_Utf8_info常量的索引,代表这个内部类的名称,如果是匿名内部类则为0 |
inner_name_access_flag | u2 | 1 | 内部类的访问标志 |
Deprecated属性
表示某个类、字段或方法不再推荐使用
Synthetic属性
表示该字段和方法是不是有Java源码直接生成的
名称 | 类型 | 数量 | 说明 |
---|---|---|---|
attribute_name_index | u2 | 1 | 属性值的索引 |
attribute_length | u4 | 1 | 属性值的长度,值必须为0x00000000 |
参考
- 深入理解Java虚拟机, 周志明
网友评论