类文件结构
《深入理解Java虚拟机(第2版)》
Java虚拟机不和包括Java在内的任何语言绑定,它只与“Class文件”这种特定的二进制文件格式锁关联。
不管是Java代码编译为存储字节码的Class文件,还是使用JRuby等其他语言的编译器将程序代码编译为Class文件,虚拟机都可以加载。
Class类文件结构
类型 | 名称 | 数量 |
---|---|---|
u4 | magic | 1 |
u2 | minor_version | 1 |
u2 | major_version | 1 |
u2 | constant_pool_count | 1 |
cp_info | constant_pool | constant_pool_count - 1 |
u2 | access_flags | 1 |
u2 | this_class | 1 |
u2 | super_class | 1 |
u2 | interfaces_count | 1 |
u2 | interfaces | interfaces_count |
u2 | fields_count | 1 |
field_info | fields | fields_count |
u2 | methods_count | 1 |
method_info | methods | methods_count |
u2 | attribute_count | 1 |
attribute_info | attributes | attributes_count |
魔数与Class文件的版本
每个Class文件的头4个字节称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。
紧接着魔数的4个字节存储的是Class文件的版本号:第5和第6字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version)。
例如CAFEBABE00000032
,其中0xCAFEBABE是魔数,次版本号的0x0000,主版本号是0x0032,也就是十进制的50.
常量池
紧接着主次版本号之后的是常量池入口,常量池可以理解为Class文件之中的资源仓库,它是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一。
常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据代表常量池容量计数值(constant_pool_count)。这里的容量计数是从1而不是0开始的。例如CAFEBABE000000320016
,其中0x0016(十进制22),这就代表常量池中有21项常量(索引值为1-21)。设计者将0项常量空出来是为了:在表达“不引用任何一个常量池项目”的含义,这种情况就可以把索引值置为0来表示。
常量池中主要存放两大类常量:字面量和符号引用。字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:
1. 类和接口全限定名
2. 字段的名称和描述符
3. 方法的名称和描述符
常量池中的数据项的类型如下表:
header 1 | header 2 |
---|---|
row 1 col 1 | row 1 col 2 |
row 2 col 1 | row 2 col 2 |
常量池中数据项类型 | 类型标志 | 类型描述 |
---|---|---|
CONSTANT_Utf8_info | 1 | UTF-8编码的Unicode字符串 |
CONSTANT_Integer_info | 3 | int类型字面值 |
CONSTANT_Float_info | 4 | float类型字面值 |
CONSTANT_Long_info | 5 | long类型字面值 |
CONSTANT_Double_info | 6 | double类型字面值 |
CONSTANT_Class_info | 7 | 对一个类或接口的符号引用 |
CONSTANT_String_info | 8 | String类型字面值 |
CONSTANT_Fieldref_info | 9 | 对一个字段的符号引用 |
CONSTANT_Methodref_info | 10 | 对一个类中声明的方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 对一个接口中声明的方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 对一个字段或方法的部分符号引用 |
CONSTANT_MethodHandle_info | 15 | 表示方法句柄 |
CONSTANT_MmethodType_info | 16 | 标志方法类型 |
CONSTANT_InvokeDynamic_info | 18 | 表示移动动态方法调用点 |
可以使用Javap命令输出常量表:
javap -verbose TestClass.class
为什么Java中方法、字段名有最大值的限制?
每个方法、字段都需要在常量池中引用CONSTANT_Utf8_info型常量来描述名称,而常量池的最大值就是(0xffff,常量池容量计数值是u2类型的,2个字节),为65535(16^4-1)。
其他
后面还有访问标志、类索引、父类索引与接口索引集合、字段表集合、方发表集合等等等等。。不一一分析了,以后有机会再详细看。