Notes - Java Class format
深入理解Class文件格式
信息描述规则
> 数据类型描述规则
原始数据类型对应的字符串描述
数据类型 | 描述 |
---|---|
byte | B |
char | C |
double | D |
float | F |
int | I |
long | J |
short | S |
boolean | Z |
引用数据类型格式为 "LClassName"
. ex: Ljava/lang/String;
. 全路径名的 “.” 由 “/“ 替代,并且最后必须要带分号(;)
数组也是一种引用类型,用 "[其他类型描述名"
表示,比如一个int数组 [I
,二维int数组 [[I
> 成员变量描述规则
…
> 成员函数描述规则
…
> access_flags介绍
Class的access_flags取值
|标志名|取值|说明|
|-|-|
|ACC_PUBLIC|0x0001|public类型|
|ACC_FINAL|0x0010|final类型|
|ACC_SUPER|0x0020|用于invokespecial指令|
|ACC_INTERFACE|0x0200|表明这个类是一个interface|
|ACC_ABSTRACT|0x0400|abstract类型|
|ACC_SYNTHETIC|0x1000|表明该类由编译器根据情况生成的,源码里无法显示定义这样的类|
|ACC_ANNOTATION|0x2000|注解类型|
|ACC_ENUM|0x4000|枚举类型|
Field的access_flags取值
标志名 | 取值 | 说明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public类型 |
ACC_PRIVATE | 0x0002 | private类型 |
ACC_PROTECTED | 0x0004 | protected类型 |
ACC_STATIC | 0x0008 | static类型 |
ACC_FINAL | 0x0010 | final类型 |
ACC_VOLATILE | 0x0040 | volatile类型 |
ACC_TRANSIENT | 0x0080 | transient类型,说明该成员不能被串行化 |
ACC_SYNTHETIC | 0x1000 | 表明该成员由编译器根据情况生成,源码里无法显示定义这样的成员 |
ACC_ENUM | 0x4000 | 枚举类型 |
Method的access_flags取值
标志名 | 取值 | 说明 |
---|---|---|
ACC_PUBLIC | 0x0001 | public类型 |
ACC_PRIVATE | 0x0002 | private类型 |
ACC_PROTECTED | 0x0004 | protected类型 |
ACC_STATIC | 0x0008 | static类型 |
ACC_FINAL | 0x0010 | final类型 |
ACC_SYNCHRONIZED | 0x0020 | synchronized函数 |
ACC_BRIDGE | 0x0040 | 桥接方法,由编译器根据情况生成 |
ACC_VARARGS | 0x0080 | 可变参数个数的函数 |
ACC_NATIVE | 0x0100 | native函数 |
ACC_ABSTRACT | 0x0400 | 抽象函数 |
ACC_STRICT | 0x0800 | strictfp类型(strict float point,精确浮点) |
ACC_SYNTHETIC | 0x1000 | 表明该成员由编译器根据情况生成的,源码里无法直接定义这样的成员(通常称之为合成函数。另外,内部类访问外部类的私有成员时,在class文件中也会生成一个ACC_SYNTHETIC修饰的函数) |
属性名称和作用
名称 | 说明 |
---|---|
“ConstantValue” | 该属性只出现于field_info中,用于描述一个常量成员域(long、float、double、int、char、short、byte、boolean、String等)的值 |
“Code” | 该属性只出现于method_info中,用于描述一个函数(非native和abstract的函数)的内容:即源码中该函数内容编译后得到的虚拟机指令,try/catch语句对应的异常处理表等 |
“Exceptions” | 当一个函数抛出异常(Exception)或错误(Error)时,这个函数的method_info将保存此属性 |
“SourceFile” | 此属性位于ClassFile的属性集合中,它包含一个指向Utf8常量项的索引,包含此Class对应的源码文件名 |
“LocalVariableTable” | 属性还可以包含属性。比如”LocalVariableTable”属性就是包含在”Code”属性中的,用来描述一个函数的本地变量相关信息。比如这个变量的名字、这个变量在源码哪一行定义的 |
结论:
1、属性的类型由其名字来描述。比如”Code”、”SourceFile”等。
2、不同类型的属性可能出现在ClassFile中不同的成员里,比如”Code”属性只能出现在method_info中。虚拟机在解析Class文件的时候是需要校验很多内容的,比如abstract的函数或native的函数就不能携带”Code”属性。
3、属性也可以包含子属性,比如”Code”属性能包含”LocalVariableTable”属性。
Structs
ClassFile Struct
1 | ClassFile { |
cp_info struct
1 | cp_info { // u1表示该域对应一个字节长度,u表示unsigned无符号的 |
常量项
常量项Utf8、Class等对应的数据结构
1 | CONSTANT_Utf8_info { |
常量项Long、Integer等对应的数据结构
1 | CONSTANT_Long_info { |
常量池
field_info
和method_info
数据结构
1 | field_info { |
access_flags
: 访问标志,成员变量和成员函数的访问标志略有不同
name_index
: 指向成员变量或成员函数名字的Utf8_info
常量项
descriptor_index
: 指向Utf8_info
常量项,其内容分别是描述成员变量的FieldDescriptor和描述成员函数的MethodDescriptor
attributes
: 属性信息,成员域和成员函数都包含若干属性
属性
1 | attribute_info { |
Code属性
1 | Code_attribute { |
attribute_name_index
: 指向内容为”Code”的Utf8_info
常量项
attribute_length
: 表示接下来内容的长度
max_stack
: JVM执行一个指令的时候,该指令的操作数存储在一个名叫”操作数栈(operand stack)“的地方,每一个操作数占一个或两个(long、double类型的操作数)栈项。
stack就是一块只能先入后出的内存。max_stack用于说明这个函数在执行过程中,需要最深多少栈空间(也就是多少栈项)。
max_locals
: 表示该函数包括最多几个局部变量
code_length
和code
: 函数对应的指令内容也就是这个函数的源码经过编译转换后得到的Java指令码存储在code数组中,其长度由code_length表示
exception_table_length
和exception_table
: 一个函数可以包含多个try/catch语句,一个try/catch语句对应exception_table数组中的一项
pc(program counter): JVM执行的时候,会维护一个变量来指向当前要执行的指令,这个变量就叫pc
start_pc
: 描述try/catch语句从哪条指令开始。注意,这个table中的各个pc变量的取值必须位于代表整个函数内容的Java字节码**code[code_length]**数组中
end_pc
: 表示这个try语句到哪条指令结束。注意,只包括try语句,不包括catch
handler_pc
: 表示catch语句的内容从哪条指令开始
catch_type
: 表示catch语句中截获的Exception或Error的名字,指向Utf8_info常量项。如果catch_type取值为0,则表示它是final{}语句块
Code_attribute还能包含其他属性:
LineNumberTable
: 用于调试,比如指明哪条指令。对应源码哪一行。
LocalVariableTable
: 用于调试,调试时可以用于计算本地变量的值。
LineNumberTable属性
1 | LineNumberTable_attribute { |
start_pc
: 指向Code_attribute中code数组某处指令
line_number
: 说明start_pc位于源码哪一行。注意,多个line_number_table元素可以指向同一行代码。因为一行Java代码很可能被编译成多条指令
LocalVariableTable属性
1 | LocalVariableTable_attribute { |
start_pc
和length
: 决定了一个局部变量在code数组中的有效范围
name_index
: 此局部变量的名字,指向Utf8_info常量项
descriptor_index
: 此局部变量的类型,也指向Utf8_info常量项,其内容是FieldDescriptor字符串描述
...
: …