JVM Class文件结构
每一个class文件对应于如下所示的结构体:
其中 u1,u2和u4是Class文件结构的私有数据类型,分别表示1个字节,2个字节和4个字节的无符号数
字段解释:
- magic
魔数,魔数的唯一作用是确定这个文件是否为一个能被虚拟机所接受的Class文件。魔数值固定为0xCAFEBABE,不会改变。
- minor_version和major_version
副版本号和主版本号,minor_version和major_version的值分别表示Class文件的副、主版本。它们共同构成了Class文件的格式版本号。譬如某个Class文件的主版本号为M,副版本号为m,那么这个Class文件的格式版本号就确定为M.m。Class文件格式版本号大小的顺序为:1.5 < 2.0 < 2.1。
一个Java虚拟机实例只能支持特定范围内的主版本号(Mi至Mj)和0至特定范围内(0至m)的副版本号。假设一个Class文件的格式版本号为V,仅当Mi.0 ≤ v ≤ Mj.m成立时,这个Class文件才可以被此Java虚拟机支持。不同版本的Java虚拟机实现支持的版本号也不同,高版本号的Java虚拟机实现可以支持低版本号的Class文件,反之则不成立
major版本对应关系:
version | major | hex |
Java SE 9 | 53 | 0x35 |
Java SE 8 | 52 | 0x34 |
Java SE 7 | 51 | 0x33 |
Java SE 6.0 | 50 | 0x32 |
Java SE 5.0 | 49 | 0x31 |
JDK 1.4 | 48 | 0x30 |
JDK 1.3 | 47 | 0x2F |
JDK 1.2 | 46 | 0x2E |
JDK 1.1 | 45 | 0x2D |
- constant_pool-count
常量池计数器,constant_pool_count的值等于constant_pool表中的成员数加1。constant_pool表的索引值只有在大于0且小于constant_pool_count时才会被认为是有效的,对于long和double类型有例外情况
- constant_pool[]
常量池,constant_pool是一种表结构(§4.4),它包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。常量池中的每一项都具备相同的格式特征——第一个字节作为类型标记用于识别该项是哪种类型的常量,称为“tag byte”。常量池的索引范围是1至constant_pool_count−1
- access_flags
访问标志,access_flags是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。access_flags的取值范围和相应含义:
ACC_PUBLIC:取值为0x0001
ACC_FINAL::取值为0x0010
ACC_SUPER:取值为0x0020,用于确定该Class文件里面的invokespecial指令使用的是哪一种执行语义。目前Java虚拟机的编译器都应当设置这个标志。ACC_SUPER标记是为了向后兼容旧编译器编译的Class文件而存在的,在JDK1.0.2版本以前的编译器产生的Class文件中,access_flag里面没有ACC_SUPER标志。同时,JDK1.0.2前的Java虚拟机遇到ACC_SUPER标记会自动忽略它
ACC_INTERFACE:取值为0x0200,表示是接口
ACC_ABSTRACT:取值为0x0400
ACC_SYNTHETIC:取值为0x1000, 表示并非Java源码生成的字节码
ACC_ANOTATION:0x2000,表示是注解
ACC_ENUM:取值为0x4000,表示是枚举
- this_class
类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类或接口
- super_class
父类索引,对于类来说,super_class的值必须为0或者是对constant_pool表中项目的一个有效索引值。如果它的值不为0,那constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类的直接父类。当前类的直接父类,以及它所有间接父类的access_flag中都不能带有ACC_FINAL标记。对于接口来说,它的Class文件的super_class项的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为代表java.lang.Object的CONSTANT_Class_info类型常量。如果Class文件的super_class的值为0,那这个Class文件只可能是定义的是java.lang.Object类,只有它是唯一没有父类的类
- interfaces_count
接口计数器,interfaces_count的值表示当前类或接口的直接父接口数量
- interfaces[]
接口表,interfaces[]数组中的每个成员的值必须是一个对constant_pool表中项目的一个有效索引值,它的长度为interfaces_count。每个成员interfaces[i] 必须为CONSTANT_Class_info类型常量,其中0 ≤ i < interfaces_count。在interfaces[]数组中,成员所表示的接口顺序和对应的源代码中给定的接口顺序(从左至右)一样,即interfaces[0]对应的是源代码中最左边的接口。
- fields_count
字段计数器,fields_count的值表示当前Class文件fields[]数组的成员个数。fields[]数组中每一项都是一个field_info结构的数据项,它用于表示该类或接口声明的类字段或者实例字段
- fields[]
字段表,fields[]数组中的每个成员都必须是一个fields_info结构的数据项,用于表示当前类或接口中某个字段的完整描述。fields[]数组描述当前类或接口声明的所有字段,但不包括从父类或父接口继承的部分
- methods_count
方法计数器,methods_count的值表示当前Class文件methods[]数组的成员个数。Methods[]数组中每一项都是一个method_info结构的数据项
- methods[]
方法表,methods[]数组中的每个成员都必须是一个method_info结构的数据项,用于表示当前类或接口中某个方法的完整描述。如果某个method_info结构的access_flags项既没有设置ACC_NATIVE标志也没有设置ACC_ABSTRACT标志,那么它所对应的方法体就应当可以被Java虚拟机直接从当前类加载,而不需要引用其它类。method_info结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、实例初始化方法方法和类或接口初始化方法方法。methods[]数组只描述当前类或接口中声明的方法,不包括从父类或父接口继承的方法
- attributes_count
属性计数器,attributes_count的值表示当前Class文件attributes表的成员个数。attributes表中每一项都是一个attribute_info结构的数据项
- attributes[]
属性表,attributes表的每个项的值必须是attribute_info结构。在本规范里,Class文件结构中的attributes表的项包括下列定义的属性:InnerClasses、EnclosingMethod、Synthetic、Signature、SourceFile,SourceDebugExtension、Deprecated、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations以及BootstrapMethods属性。对于支持Class文件格式版本号为49.0或更高的Java虚拟机实现,必须正确识别并读取attributes表中的Signature、RuntimeVisibleAnnotations和 RuntimeInvisibleAnnotations属性。对于支持Class文件格式版本号为51.0或更高的Java虚拟机实现,必须正确识别并读取attributes表中的BootstrapMethods属性。本规范要求任一Java虚拟机实现可以自动忽略Class文件的attributes表中的若干(甚至全部)它不可识别的属性项。任何本规范未定义的属性不能影响Class文件的语义,只能提供附加的描述信息