Class文件结构
结构总览
Class文件中只有两种数据类型:无符号数和表:
- 无符号数:属于基本的数据类型,以u1,u2,u4,u8代表对应的字节数;
- 表:是由多个无符号数或者其他表作为数据项组合而成的复合数据结构。
1.magic(u4)
魔数,固定为0xCAFEBABE,唯一的作用是确定这个文件是否为一个能被虚拟机接受的class文件;
2.minor_version(u2) 和 major_version(u2)
minor_version:class文件次版本号;
major_version:class文件主版本号
3.constant_pool_count(u2) 和 constant_pool(cp_info)
由于常量池的数量是不固定的,所以需要在前面放置一个constant_pool_count(u2)表示常量池的数量(注意常量池的计数是从1开始的,设计者将第0项空出来是为了满足后面要表示“不引用任何一个常量池项目”)。
常量池主要存放两大类常量:
- 字面量:比如文本字符串,声明为final的常量值,文件名等;
- 符号引用:类和接口的全限定名;字段的名称和描述符;方法的名称和描述符.
Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接,根据实际运行的类型将对应的符号引用解析成具体的内存地址;也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息。
4.access_flags(u2)
类或接口的访问标志,包括:
- 这个Class是类还是接口?
- 是否public?
- 是否abstract?
- 是否final?
等
5.this_class(u2) 和 super_class(u2)
- this_class:类索引;
- super_class:父类索引;
这两个索引都会指向常量池中类型为CONSTANT_Class_info,其中Object类的super_class为0。
6.interfaces_count(u2) 和 interfaces
- interfaces_count表示接口数量;
- interfaces是接口的集合,内部每个条目是u2类型指向常量池;
7.fields_count(u2) 和 fields
-
access_flags表示一些访问标识符
-
name_index 表示字段的名称,指向常量池的下标;
-
descriptor_index 表示字段描述符,指向常量池的下标;
-
attributes_count(u2) 和 attributes
字段的属性集合,可以存储一些额外的信息,可以包含零个或多个;
由于在Class级别也有attributes属性,后面再详细介绍。
8.methods_count(u2) 和 methods
方法的描述和字段的描述采用了一样的结构,如下所示:
和字段表不同的是,access_flags的取值不同,因为volatile和transient不能修饰方法,所以方法的access_flags中没有ACC_VOLATILE和ACC_TRANSIENT,同时方法相对于字段多了Synchronized、native、abstract等,所以多了ACC_SYNCHRONIZED、ACC_NATIVE、ACC_ABSTRACT等值.
还有一个最大的区别是:字段表的attribute里面多了一个Code字段;
方法体中的代码经过Javac编译后,最终变为字节码存储到Code属性内;只有拥有方法体的方法才会有Code属性,接口的方法和抽象方法是没有的。
这里介绍主要的属性:
- max_stack:表示操作数栈(Operand Stacks)的最大深度,虚拟机运行的时候需要根据这个值栈帧(Stack Frame)中的操作数栈深度;
- max_locals:表示局部变量表所需的空间,max_locals的单位是Slot
Slot是虚拟机为局部变量分配内存所使用的最小单位; 对于byte、char、float、int、short、boolean、returnAddress等长度不超过32位的数据类型,每个局部变量占用1个Slot; 对于double和long这两种64位的数据类型占2个Slot。
方法参数(包括实力方法中的隐藏参数this)、try-catch语句中的catch块中所定义的异常、方法体中定义的局部变量都需要存放在局部变量表里面;
值得注意的:max_locals的值并不等于上述各项之和,原因是局部变量表中的Slot是可以重用的,当代码执行超出一个变量的作用域时,这个局部变量所占的Slot可以被其他局部变量使用。
-
code:用来存储编译后生成的字节码指令,每个指令就是一个u1类型的数据;
字节码所对应的指令含义是虚拟机事先定义好的,当虚拟机读取到code中的一个字节码时,就可以找出对应的指令,配合操作数栈完成指令内容。 -
exception_table:用来表示显示的异常处理表,对应于代码中的try-catch语句;
异常表实际上是Java代码的一部分,编译器使用异常表而不是简单的跳转命令来实现Java异常及finally处理机制;
其中包括了4个字段,表示含义为:虚拟机在执行字节码的时候,如果在[start_pc,end_pc)行之间出现了catch_type的异常,则程序会跳转到handler_pc行继续处理;
当catch_type的值为0时,代表除了catch_type的任意异常情况都要跳转到handler_pc处继续进行。 -
Exceptions:表示方法可能抛出的异常;
这里的Exceptions属性是在方法表中与Code属性平级的,不是上面的exception_table;
9.attributes属性
该属性可用的选项比较多,常用的有:
- LineNumberTable:用于描述Java源代码和字节码行号的对应关系,不是运行时必须的;
- LocalVariableTable:用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,不是运行时必须的;
- SourceFile:Class文件的源码文件的文件名称;