1.Class文件格式
根据Java虚拟机描述,Class文件中数据的排列并不需要严格的字节对齐,因此字节与字节之间没有"空隙",各个字节所处的位置以及所表达的含义都是严格限定的,对于多字节数据,严格按照大端模式存储。
能够被JVM识别并执行的Class文件或者网络字节流应该具有如下格式:
1.1、magic
magic为JVM所能够识别的魔术,占用4个字节空间,此值固定的十六进制格式数据位:0xCAFEBABE,谐音:咖啡Baby。
1.2、主次版本号
主次版本号用于确定该类文件能够在哪些版本的虚拟器上运行。
1.3、常量池
常量池存放字符串常量、类或者接口的名称等常量信息。
1.4、访问标志
决定类文件访问权限的掩码值。
1.5、this\supper class
表示类以及类父类信息。
1.6、interfaces
当前类或者接口的直接父接口。
1.7、fields
当前类或者接口测字段描述信息,不包括父类或者父接口中的字段信息。
1.8、methods
当前类或者接口的方法描述,不考考父类或者父接口中的方法描述。
1.9、attributes
描述Class文件的属性信息。
2.常量池
2.1、常量池结构
常量池中存放如下14中类型的常量信息,常量池的单项结构可以通过cp_info表示,tag为标记,取值范围如下所示,不同的tag值对应不同的常量项结构。
2.2、Utf8
Utf8结构用于表示字符串常量的值。
tag值为1,表示CONSTANT_Utf8。
length表示bytes数组的长度。
bytes[]表示存储字符串值得byte数组,每个byte的值不能够是0,也不能够是0xf0~0xff之间的数。
'\u0001'到
'\u007f'之间的字符使用单字节表示,从
'\u0080'到
'\u07ff'之间的字符用双字节表示,从
'\u0800'到
'\uffff'之间的字符,用三字节表示,高于'\uffff'的字符,用6字节表示,表示方式可以参考Java虚拟机规范一书。
在Hotspot中,与Utf8编码相关的接口位于:\hotspot\src\share\vm\utilities\utf8.cpp,具体如下:
2.3、Integer
在Hotspot中,与Utf8编码相关的接口位于:\hotspot\src\share\vm\utilities\utf8.cpp,具体如下:
bool UTF8::is_supplementary_character(const unsigned char* str) { return ((str[0] & 0xFF) == 0xED) && ((str[1] & 0xF0) == 0xA0) && ((str[2] & 0xC0) == 0x80) && ((str[3] & 0xFF) == 0xED) && ((str[4] & 0xF0) == 0xB0) && ((str[5] & 0xC0) == 0x80); } jint UTF8::get_supplementary_character(const unsigned char* str) { return 0x10000 + ((str[1] & 0x0f) << 16) + ((str[2] & 0x3f) << 10) + ((str[4] & 0x0f) << 6) + (str[5] & 0x3f); } int UNICODE::utf8_size(jchar c) { if ((0x0001 <= c) && (c <= 0x007F)) return 1; if (c <= 0x07FF) return 2; return 3; } // Writes a jchar a utf8 and returns the end static u_char* utf8_write(u_char* base, jchar ch) { if ((ch != 0) && (ch <=0x7f)) { base[0] = (u_char) ch; return base + 1; } if (ch <= 0x7FF) { /* 11 bits or less. */ unsigned char high_five = ch >> 6; unsigned char low_six = ch & 0x3F; base[0] = high_five | 0xC0; /* 110xxxxx */ base[1] = low_six | 0x80; /* 10xxxxxx */ return base + 2; } /* possibly full 16 bits. */ char high_four = ch >> 12; char mid_six = (ch >> 6) & 0x3F; char low_six = ch & 0x3f; base[0] = high_four | 0xE0; /* 1110xxxx */ base[1] = mid_six | 0x80; /* 10xxxxxx */ base[2] = low_six | 0x80; /* 10xxxxxx */ return base + 3; }
2.3、Integer
表示整数的常量结构项。
tag为3,表示CONSTANT_Integer。
bytes占用4个字节,表示整数的具体指,存储方式为大端存储。
2.4、Float
按照IEEE 754单精度浮点格式表示的浮点数常量结构项。
tag为4,表示CONSTANT_Float。
bytes为存储浮点数的字节数组,以大端模式存储。
2.5、Long
长整形的常量结构项。
tag为5,表示CONSTANT_Long。
high_bytes和low_bytes表示组成64bit的Long型整数的高32位和低32位值,high_bytes和low_bytes分别以大端模式存储,通过((long) high_bytes << 32) + low_bytes方式构建64bit的整数。
2.6、Double
双精度浮点数常量结构项。
双精度浮点数占用64bit,分别由两个4字节数组构成,换算方式见参加Java虚拟机规范。
2.7、Class
用于表示类或者借口的常量项。
tag为7,表示CONSTANT_Class。
name_index为一个2字节16位无符号整数,指向常量池中一个合法的索引,在该索引处存放CONSTANT_Utf8_info的常量项,代表Class的类名称或者接口名称。
2.8、String
用于表示字符串的常量项结构。
tag为8,表示CONSTANT_String。
string_index为两个字节16位的无符号整数,指向常量池中的有效索引,该索引处存放CONSTANT_Utf8_info类型的常量项,表示一组Unicode编码的序列,该序列最后用于字符串的初始化操作。
2.9、Field
用于表示字段的常量项结构。
tag为9,表示CONSTANT_Fieldref。
class_index为u2型整数,指向常量池中的有效索引,该索引处存放CONSTANT_Class_info常量项,描述此字段所属的类或者接口(类或者接口皆可)。
name_and_type_index为u2型整数,指向常量池的有效索引,该索引处存放CONSTANT_NameAndType_info,表示当前字段的描述符。
2.10、Method
用于表示方法的常量项结构。
tag为10,表示CONSTANT_Methodref。
class_index为u2型整数,指向常量池中的有效索引,该索引处存放CONSTANT_Class_info常量项,描述此方法所属的类(不能够是接口)。
name_and_type_index为u2型整数,指向常量池的有效索引,该索引处存放CONSTANT_NameAndType_info,表示当前方法的描述符。如果方法以“<”('\u003c')开头,则说明这个方法名是特殊的<init>,即这个方法是实例初始化方法,它的返回类型必须为空。
2.11、InterfaceMethod
表示接口方法的常量项结构。
tag为11,表示CONSTANT_InterfaceMethodref。
class_index为u2型整数,指向常量池中的有效索引,该索引处存放CONSTANT_Class_info常量项,描述此方法所属的接口(不能是类)。
name_and_type_index为u2型整数,指向常量池的有效索引,该索引处存放CONSTANT_NameAndType_info,表示当前方法的描述符。
2.12、NameAndType
描述字段或者方法的常量项结构。
tag为12,表示CONSTANT_NameAndType。
name_index为u2类型的指向常量池的有效索引,该索引处存放CONSTANT_Utf8_info常量项,表示特殊方法<init>,或者表示字段、方法的非全限定名。
descriptor_index和name_index一样,也是常量池中的一个Utf8常量项,表示字段或者方法的描述符。
2.13、MethodHandle
描述方法句柄的常量项结构。
tag为15,表示CONSTANT_MethodHandle。
reference_kind为单字节无符号整数,值为1~9之间的数字,标记方法的字节码行为。
reference_index为u2类型整数,常量池的有效索引,根据reference_kind值的不同,该索引处存放的常量项可以使CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info。
reference_kind的类型以及对应reference_index处的常量项类型如下:
reference_kind值在1~4之间时,reference_index处所存放的常量项类型为CONSTANT_Fieldref_info,表示由一个类或者接口的字段创建的方法句柄。
reference_kind值在5~8之间时,reference_index处所存放的常量项为CONSTANT_Mehtodref_info,表示由一个类的方法或者类的构造函数创建的方法句柄。
reference_kind为9的时候,reference_index处所存放的常量项为CONSTANT_InterfaceMethodref_info,表示由一个接口的方法创建的方法句柄。
refernece_kind的值为5、6、7、9时,reference_index处所存放的常量项所表示的方法不能够是实例初始化(<init>)方法或类初始化方法(<clinit>)。
referenct_kind的值为8的时候,reference_index处所存放的常量项CONSTANT_Mehtodref_indo所描述的方法必须是实例初始化(<init>)方法。
2.14、MethodType
描述方法类型的常量项结构。
tag为16,表示CONSTANT_MehtodType。
descriptor_index为u2类型的指向常量池有效索引的整数,该索引处存放的是CONSTANT_Utf8_info常量项,表示该方法的描述符。
2.15、InvokeDynamic
表示invokedynamic指令所使用到的引导方法(Bootstrap Method)的相关信息,包括引导方法所使用到的动态调用名称(Dynamic Invocation Name)、参数、返回值类型、以及可以选的被称为静态参数(Static Arguments)的常量序列。
tag为18,表示CONSTANT_InvokeDynamic。
bootstrap_method_attr_index为u2类型的整数,为当前class文件的引导方法数组bootstrap_methods[]的一个有效下标。
name_and_type_index为u2类型的常量池索引,该索引处存放CONSTANT_NameAndType_info常量项,表示方法的名称和方法的描述符。
3.definition
CONSTANT_Utf8_info {u1 tag;u2 length;u1 bytes[length];}
CONSTANT_Integer_info {u1 tag;u4 bytes;}
CONSTANT_Float_info {u1 tag;u4 bytes;}
CONSTANT_Long_info {u1 tag;u4 high_bytes;u4 low_bytes;}
CONSTANT_Double_info {u1 tag;u4 high_bytes;u4 low_bytes;}
CONSTANT_Class_info {u1 tag;u2 name_index;}
CONSTANT_String_info {u1 tag;u2 string_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_NameAndType_info {u1 tag;u2 name_index;u2 descriptor_index;}
CONSTANT_MethodHandle_info {u1 tag;u1 reference_kind;u2 reference_index;}
CONSTANT_MethodType_info {u1 tag;u2 descriptor_index;}
CONSTANT_InvokeDynamic_info {u1 tag;u2 bootstrap_method_attr_index;u2 name_and_type_index;}