1 简介
Class文件采用类C语言结构体的伪结构存储数据,这种结构包含两种数据类型:无符号数和表
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.1 无符号数
属于基本数据类型,以u1、u2、u4、u8代表1个字节、2个字节、4个字节、8个字节的无符号数
1.2 表
由多个无符号数或其它表作为数据项构成的复合数据类型,所以表以 _info 结尾
注:
Class结构中没有任何分隔符号,数据存储的字节序都是严格限定,不允许改变
2 魔数(magic)
每个Class文件前4个字节代表魔数(magic number),用来进行身份的识别(通过魔数区分文件类型),Java中魔数固定为 0xCAFEBABE
3 版本(version)
3.1 次版本号(minor_version)
第5个和第6个字节代表次版本号
3.2 主版本号(major_version)
第7个和第8个字节代表主版本号,Java的主版本号从45开始,每次升级大版本将对主版本号加1,高版本兼容低版本。
编译器版本 | 主版本号 |
---|---|
1.1 | 45 |
1.2 | 46 |
1.3 | 47 |
1.4 | 48 |
5.0 | 49 |
6 | 50 |
7 | 51 |
8 | 52 |
9 | 53 |
10 | 54 |
11 | 55 |
12 | 56 |
13 | 57 |
4 常量池(constant pool)
紧接着主次版本号之后是常量池。由于常量池数量不固定,所以2个字节的constant_pool_count来代表常量池的大小。容器的计数从1开始,0代表是不引用任何常量池。常量池主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References),包括:
- 类和接口全限定名
- 字段名称和描述符
- 方法名称和描述符
常量池中的每一项都是一个表(info),这些表的第一位为u1类型的标志位(tag),代表这个常量属于哪种类型
类型 | 标志 | 描述 |
---|---|---|
CONSTANT_Utf8 | 1 | UTF-8编码的字符串 |
CONSTANT_Integer | 3 | 整型字面量 |
CONSTANT_Float | 4 | 浮点型字面量 |
CONSTANT_Long | 5 | 长整型字面量 |
CONSTANT_Double | 6 | 双精度浮点型字面量 |
CONSTANT_Class | 7 | 类或接口的符号引用 |
CONSTANT_String | 8 | 字符串类型的符号引用 |
CONSTANT_Fieldref | 9 | 字段类型的符号引用 |
CONSTANT_Methodref | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType | 12 | 字段或方法的部分符号引用 |
CONSTANT_MethodHandle | 15 | 方法句柄 |
CONSTANT_MethodType | 16 | 方法类型 |
CONSTANT_Dynamic | 17 | 动态计算常量 |
CONSTANT_InvokeDynamic | 18 | 动态方法调用点 |
CONSTANT_Module | 19 | 一个模块 |
CONSTANT_Package | 20 | 一个模块中开放或导出的包 |
4.1 CONSTANT_Utf8_info
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
- tag:固定值为1
- length:代表UTF8字符串的长度(单位字节)
- bytes:存放UTF8字符串的内容
4.2 CONSTANT_Integer_info
CONSTANT_Integer_info {
u1 tag;
u4 bytes;
}
- tag:固定值3
- bytes:按照高位在前存储int值
4.3 CONSTANT_Float_info
CONSTANT_Float_info {
u1 tag;
u4 bytes;
}
- tag:固定值4
- bytes:按照高位在前存储float值
4.4 CONSTANT_Long_info
CONSTANT_Long_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
- tag:固定值5
- bytes:按照高位在前存储long值
4.5 CONSTANT_Double_info
CONSTANT_Double_info {
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
- tag:固定值6
- bytes:按照高位在前存储double值
4.6 CONSTANT_Class_info
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
- tag:固定值为7
- name_index:指向常量项索引,表示类全限定名
4.7 CONSTANT_String_info
CONSTANT_String_info {
u1 tag;
u2 string_index;
}
- tag:固定值为8
- string_index:指向字符串字面量的索引
4.8 CONSTANT_Fieldref_info
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
- tag:固定值9
- class_index:指向声明方法的接口索引项,类型为CONSTANT_Class_info
- name_and_type_index:指向名称及类型的索引项,类型为CONSTANT_NameAndType_info
4.9 CONSTAONT_Methodref_info
CONSTANT_Methodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
- tag:固定值10
- class_index:指向声明方法的接口索引项,类型为CONSTANT_Class_info
- name_and_type_index:指向名称及类型的索引项,类型为CONSTANT_NameAndType_info
4.10 CONSTANT_InterfaceMethodref_info
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
- tag:固定值11
- class_index:指向声明方法的接口索引项,类型为CONSTANT_Class_info
- name_and_type_index:指向名称及类型的索引项,类型为CONSTANT_NameAndType_info
4.11 CONSTANT_NameAndType_info
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptor_index;
}
- tag:固定值12
- name_index:字段或方法名的常量项的索引
- descriptor_index:类型描述,表示方法签名或字段的类型
类型的字符串表示方法
字符串 | 类型 |
---|---|
B | byte |
D | double |
I | int |
S | short |
V | void |
C | char |
F | float |
J | long |
Z | boolean |
L; | 对象 |
[ | 数组 |
注:
1)对于对象类型,以L + 类全限定名 + 分号(;),例如:Ljava/lang/Object; 表示类 java.lang.Object
2)对于数组类型,以左中括号([)作为标记,例如:String二维数组,使用 [[Ljava.lang.String; 表示
4.12 CONSTANT_MethodHandle_info
CONSTANT_MethodHandle_info {
u1 tag;
u1 reference_kind;
u2 reference_index;
}
- tag:固定值15
- reference_kind:值为1至9之间,表示方法句柄的类型
- reference_index:值为常量池的有效索引
4.13 CONSTANT_Dynamic_info
CONSTANT_Dynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
- tag:固定值17
- bootstrap_method_attr_index:值为当前Class文件引导方法表的bootstrapmethods[]数组的索引
- name_and_type_index:指向常量池的索引,表示方法名和方法描述(类型为CONSTANT_NameAndType_info结构)
4.14 CONSTANT_InvokeDynamic_info
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
- tag:固定值18
- bootstrap_method_attr_index:值为当前Class文件引导方法表的bootstrapmethods[]数组的索引
- name_and_type_index:指向常量池的索引,表示方法名和方法描述(类型为CONSTANT_NameAndType_info结构)
4.15 CONSTANT_Module_info
CONSTANT_Module_info {
u1 tag;
u2 name_index;
}
- tag:固定值19
- name_index:指向常量池的索引,表示模块名
4.16 CONSTANT_Package_info
CONSTANT_Package_info {
u1 tag;
u2 name_index;
}
- tag:固定值20
- name_index:指向常量池的索引,表示包名称
5 访问标志(acccess_flags)
紧跟跟常量池之后的2个字节代表访问标志,用来识别类或接口的访问信息,包括:Class是类还是接口,是否是public类型,是否是abstract类型,是否被声明为final等,access_flags一共有16个标志位可以用(最大值0xFFFF),当前只定义了9个,没有使用的标志位要求一律为零
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public类型 |
ACC_FINAL | 0x0010 | 是否声明为final |
ACC_SUPER | 0x0020 | 是否使用增强的方法调用父类方法 |
ACC_INTERFACE | 0x0200 | 是否为接口 |
ACC_ABSTRACT | 0x0400 | 是否为抽象类 |
ACC_SYNTHETIC | 0x1000 | 是否是编译器产生的类,没有源码对应 |
ACC_ANNOTATION | 0x2000 | 是否是注解 |
ACC_ENUM | 0x4000 | 是否是枚举 |
ACC_MODULE | 0x8000 | 是否是模块 |
6 类(this_class)、父类(super_class)、接口(interfaces)
类索引、父类索引、接口索引定义如下:
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
类索引、父类索引、接口索引均使用u2类型的索引值,指向CONSTAINT_Class_info,并找到全限定名字符串。interfaces_count代表实现接口的数量,如果没有实现接口则为0。
7 字段(fields)
在接口描述之后,是类的字段信息,由于一个类会有多个字段,所以需要指出字段的个数,字段定义如下:
u2 fields_count;
field_info fields[fields_count];
字段的数量由u2类型的fields_count决定,每一个字段均为field_info结构,field_info结构如下:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- access_flags
访问标志和类的访问标志高度类似,具体如下
标志名称 | 标志值 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否是public |
ACC_PRIVATE | 0x0010 | 是否是private |
ACC_PROTECTED | 0x0020 | 是否是protected |
ACC_STATIC | 0x0200 | 是否是static |
ACC_FINAL | 0x0400 | 是否是final |
ACC_VOLATILE | 0x1000 | 是否是volatile |
ACC_TRANSIENT | 0x2000 | 是否是transient |
ACC_SYNTHETIC | 0x4000 | 是否是编译器自动产生 |
ACC_ENUM | 0x8000 | 是否是枚举 |
- name_index:2个字节的整数,表示字段的名称索引,指向常量池中的CONSTANT_Utf8
- descriptor_index:2个字节的整数,表示字段的类型索引,指向常量池中的CONSTANT_Utf8,具体参见4.11
- attributes_count:一个字段可以有一些属性,比如初始化值,注释信息等,属性的个数存放在attributes_count中
- attributes:属性的具体内容存放在attributes数组中,当为常量属性时,结构为
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;
}
- attribute_name_index:指向常量池,字符串值为"ConstantValue"
- attribute_length:4个字节组成,表示属性的长度
- constantvalue_index:指向常量池,代表常量的值类型
8 方法(methods)
在字段之后,就是类的方法,方法的结构与字段相似,具体结构如下:
u2 methods_count;
method_info methods[methods_count];
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- method_count:代表类中的方法个数
- methods:代表方法的具体结构
- 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 | 是否是本地方法 |
ACC_ABSTRACT | 0x0400 | 是否是抽象方法 |
ACC_STRICT | 0x0800 | 是否是浮点模式为FP-strict |
ACC_SYNTHETIC | 0x1000 | 是否是编译器产生的方法,没有原码对应 |
- name_index:方法的名称,指向常量池
- descriptor_index:方法的签名,指向常量池
- attributes_count:属性的个数
- attributes:属性详情
属性 | 作用 |
---|---|
Code | 表示方法的字节码 |
StackMapTable | Code属性的描述属性,用于字节码变量类型验证 |
Exceptions | 方法的异常信息 |
SourceFile | 类文件的属性,表示生成这个类的源码 |
LineNumberTable | Code属性的描述属性,描述行号和字节码的对应关系 |
LocalVariableTable | Code属性的描述属性,描述函数局部变量表 |
BootstrapMethods | 类文件的描述属性,存放类的引导方法,用于invokeDynamic |
8.1 Code
方法的主要内容存放在属性中,在属性里面最重要的一个属性就是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;
exception_info exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
exception_info {
u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
}
- attribute_name_index:常量池索引,固定值为"Code"
- attribute_length:属性值的长度
- max_stack:操作数栈的最大深度
- max_locals:局部变量表所需的最大空间
- code_length:字节码长度
- code[code_length]:字节码内容
- exception_table_length:异常表项数量
- exception_table[exception_table_length]:异常表项内容
- attribute_count:属性长度
- attributes[attributes_count]:属性内容
code属性本身也包含更多信息,包括行号、局部变量,主要包括如下信息:
- LineNumberTable
- LocalVariableTable
- StackMapTable
8.2 StackMapTable
StackMapTable中含有若干个栈映射帧(Stack Map Frame)的数据,不包含运行时所需要的信息,仅用作Class文件的类型校验,结构如下:
StackMapTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_entries;
stack_map_frame entries[number_of_entries];
}
union stack_map_frame {
same_frame;
same_locals_1_stack_item_frame;
same_locals_1_stack_item_frame_extended;
chop_frame;
same_frame_extended;
append_frame;
full_frame;
}
每个栈映射帧是为了说明在一个特定的字节码偏移位置上,系统的数据类型是什么,包括局部变量表的类型和操作数栈的类型。
8.3 LineNumberTable
LineNumberTable用于记录字节码偏移量和行号的对应关系,结构如下:
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
line_number_info line_number_table[line_number_table_length];
}
line_number_info {
u2 start_pc;
u2 line_number;
}
8.4 LocalVariableTable
这个属性也叫局部变量表,记录了一个方法中所有的局部变量,结构如下:
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
local_variable_info local_variable_table[local_variable_table_length];
}
local_variable_info {
u2 start_pc;
u2 length;
u2 name_index;
u2 index;
}
附:
附1 属性
属性表(attribute_info)结构如下:
attrbiute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
每一个属性都必须有一个名称attribute_name_index,指向CONSTANT_Utf8_info常量,通过这个常量,可以确定该属性的info结构