目录
Class文件格式总览
每一个class文件都对应着唯一一个类或接口的定义信息。
每个class文件都是由字节流组成,每个字节含有8个二进制位,多字节数据项总是按照big-endian(大端模式)的顺序存储。
u4表示4字节
ClassFile {
u4 magic 魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的class文件——魔数值固定(0xCAFEBABE)
u2 minor_version 副版本号
u2 major_version 主版本号
u2 constant_pool_count 常量池计数器 常量池表的索引值只有在大于0小于constant_pool_count才认为有效。
cp_info constant_pool 常量池 包含class文件结构及其子结构中引用的所有字符串常量,类或接口名和字段名,其它常量。
u2 access_flags 访问标志 由一组标志所构成的掩码,用于表示某个类或接口的访问权限和属性
u2 this_class 类索引,指向类名 必须是对常量池表中某项的一个有效索引值。
u2 super_class 父类索引,指向父类类名 要么是0(超类Object),要么是常量池表中某项的一个有效索引值。
u2 interface_count 接口计数器,表示当前类或接口的直接超接口数量
u2 interfaces[] 接口表 每个成员的值都必须是对常量池表中某项的有效索引值。接口顺序和对应源代码中给定的接口顺序一样。
u2 fields_count 字段计数器,表示当前class文件的fields表的成员个数
field_info fields[] 字段表 该类和接口所声明的类字段或者实例字段,不包括父类或接口
u2 method_count 方法计数器,表示当前class文件methods表的成员个数
method_info methods[] 方法表,当前类中方法。不包含超类
u2 attributes_count 属性计数器
attribute_info attributes[]
}
常量池(Constant Pool)
对应的数据结构伪代码就是一个类型为cp_info的数组。每一个cp_info对象存储了一个常量项。
cp_info { //u1表示该域对应一个字节长度,u表示unsigned
u1 tag; //每一个cp_info的第一个字节表明该常量项的类型
u1 info[]; //常量项的具体内容
}
常量项的类型
常量项的类型结构
CONSTANT_Utf8_info{
u1 tag;
u2 length;
u1 bytes[length];
}
CONSTANT_String_info{
u1 tag;
u2 string_index; //index索引到一个Utf8_info元素 ,利用索引可以减少Class文件占用空间
}
CONSTANT_Class_info{
u1 tag;
u2 name_index;
}
CONSTANT_MethodType_info{
u1 tag;
u2 descriptor_index;
}
CONSTANT_Fieldref_info{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Long_info{
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Double_info{
u1 tag;
u4 high_bytes;
u4 low_bytes;
}
CONSTANT_Interger_info{
u1 tag;
u4 bytes;
}
CONSTANT_Float_info{
u1 tag;
u4 bytes;
}
数据类型描述规则
1.原始数据类型:"B""C""D""F""I""J""S""Z",分别对应的Java类型为byte、char、double、float、int、long、short、boolean。
2.引用数据类型:"LClassName;",ClassName为对应类的全路径名,比如上"Ljava/lang/String;"
3.数组类型:"[其他类型的描述名",比如一个int数组的描述为"[I",一个字符串数组的描述为"[Ljava/lang/String;",一个二维int数组的描述为"[[I"。
成员变量描述规则
成员变量描述只包含FieldType一种信息,FieldType为数据类型
成员函数描述规则
函数描述包括两个部分,括号内的是参数的数据类型描述,可有0到多个,紧接右括号的是返回值类型描述
如:(Ljava/lang/String;)V。
字段和方法的数据结构
field_info{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Method_info{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
access_flags
attribute_info
属性也分很多类型,由其名称来区别
attribute_info {
u2 attribute_name_index; // 属性名称,指向常量池中Utf8常量项的索引
u4 attribute_length; // 该属性具体内容的长度,即下面info数组的长度
u1 info[attribute_length]; // 属性具体内容
}
常见的属性
Code属性
Code_attribute {
u2 attribute_name_index; //指向内容为"Code"的Utf8_info常量项
u4 attribute_length; //接下来内容的长度
u2 max_stack; //说明这个函数在执行过程中,需要最深多少栈空间
u2 max_locals; //该函数包括最多几个局部变量
u4 code_length;
u1 code[code_length]; //函数的源码经过编译器转换后得到的Java指令码存储在code数组
u2 exception_table_length; //一个try/catch语句对应exception_table数组中的一项
{ u2 start_pc; //描述try/cath语句从哪条指令开始,指向code数组某处指令
u2 end_pc; //try语句到哪条指令结束
u2 handler_pc; //try语句到哪条指令结束
u2 catch_type; //catch中截获的Exception或Error的名字,指向Utf8_info常量项。如果catch_type取值为0,则表示它是final{}语句块
} exception_table[exception_table_length];
u2 attributes_count; //还能包含其他属性
attribute_info attributes[attributes_count];
}
Code_attribute里还能包含的其他常见属性有:
·LineNumberTable用于调试,比如指明哪条指令。对应于源码哪一行。
·LocalVariableTable用于调试,调试时可以用于计算本地变量的值。
·LocalVariableTypeTable,功能和LocalVariableTable类似。
·StackMapTable为Java 1.6以上才支持的属性。JVM加载Class文件的时候,将利用该属性的内容对函数进行类型校验(Type Checking)。
code数组只能包括Java指令码和操作数
JVM规范中定义的Java指令码的个数不超过255个
Java指令码长度为一个字节。指令码后面跟0或多个参数(占N个字节大小,N>=0)。由规范定义好。
LineNumberTable_attribute
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc; //指向Code_attribute中code数组某处指令
u2 line_number; //说明start_pc位于源码的哪一行
} line_number_table[line_number_table_length];
}
LocalVariableTable
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc; //局部变量在code数组中的有效起始
u2 length; //局部变量在code数组中的有效范围
u2 name_index; //此局部变量的名字,指向Utf8_info常量项
u2 descriptor_index; //此局部变量的类型,也指向Utf8_info常量项
u2 index;
} local_variable_table[local_variable_table_length];
}
每个非static函数都会自动创建一个叫作this的本地变量,代表当前是在哪个对象上调用此函数。注意,this对象位于局部变量数组第1个位置(Slot=0)。this作用范围贯穿整个函数,所以从Start=0开始,作用范围为Length=20。