JVM的Class文件结构
作为类(或者接口)信息的载体,每个class文件都完整地定义了一个类。为了使Java程序可以“编写一次,处处运行”,Java虚拟机规范对class文件格式进行了严格的规定。
构成class
文件的基本数据单位是字节,可以把整个class文件当成一个字节流来处理。稍微大一些的数据由连续多个字节构成,这些数据在class文件中以大端(big-endian)顺序存放,即高位字节在前。为了描述class文件格式,《Java虚拟机规范》定义了u1
、u2
和u4
三种数据类型来表示1、2和4字节无符号整数。而相同类型的多条数据一般存储在表(table)中,表(table)由表头和表项组成,表头是u2
或u4
。假设表头的值为3
,则table的大小就为3
(后面会紧跟3
个表项)。
下面是《Java虚拟机规范》定义的class文件格式:
-
magic
:(魔数)标识class文件格式的作用,具有固定值0xCAFEBABE
-
minor_version
,major_version
:这两项是产生该class文件的编译器的副版本和主版本号。JVM就是通过这两个值判断自己是否可以运行当前class文件的。其中,JDK8的魔数是52
。 -
constant_pool_count
:必须大于0,标识constant_pool
的大小。 -
constant_pool[]
:这是常量池数组,保存ClassFile及其子结构中引用的各种字符串常数,类名和其他常数的边长结果。值得注意的是,constant_pool
的索引是1-(n-1)的,constant_pool[0]
是保留为Java虚拟机内部使用的,不会出现在ClassFile中。
-
access_flags
:是类和接口声明中使用的修饰符的掩码,标志着当前类的访问权限(Public,Final。。。) -
this_class
:这项的值是对constant_pool
的一个索引,指向常量池中的某一项,而这一项一定会是一个CONSTANT_class_info
结构,一般是当前类的全限定名。 -
supper_class
:从字面就可以看出这是关于超类的定义,除了java.lang.Object
(supper_class
会是0)其它类都会有一个超类,也是指向常量池的一个有效索引。 -
interfaces_count
:给出该类或者接口类型的直接超接口的个数。(也就是该类实现的所有接口的个数) -
interfaces[]
:每个值也都是对常量池的索引。比如下图是java.lang.String.class
的interfaces
属性
-
fields_count
:是fields标中field_info
结构的个数。 -
fields[]
:每个值是当前类或接口定义的字段信息,每一个项如下图。
下图是JRE1.8的java.lang.String.class
文件的value内容,其中name_index
和descriptor_index
指向的都是常量池中的索引,也就是name_index的字面量是constant_pool
中的第210项。 -
descriptor_index
是描述符,《Java虚拟机规范》对此有清晰的定义:- 基本类型byte、short、char、int、long、float和double描述符是单个字母,分别是B、S、C、I、J、F和D。
- 引用类型的描述符是
L+类的完全限定名+分号
。 - 数组类型是
[+数组元素类型描述符
。 - 方法相关的描述符是
(分号分隔的参数类型描述符)+返回值类型描述符
,其中void返回值由单个字母V
表示。
-
methods_count
和methods
:表示该类的类和接口中的方法,methods
中的项结构和fields
的大致相同,如下图。
-
attributes_count
和attributes
:属性表是一个大杂烩,里面存储了各式各样的信息。这部分循序自定义属性,并且运行JVM在运行的时候忽略它们不能识别的属性值。
备注
我这里跟着《自动动手写Java虚拟机》一书的第三章节实现了ClassFile的解析,有兴趣的同学可以移步到代码地址。自己动手实现一个简单版本后,确实对JVM的理解更加深入一些。
参考
- 《自己动手写Java虚拟机》-- 第三章 解析class文件
- 《Java虚拟机规范》