《深入Java虚拟机》导读之五: Class文件结构

时间:2022-12-26 16:43:07

class文件是为java程序精确定义的二进制文件格式. 正是因为这种精确的定义, 使得无论在任何平台或程序上产生的class文件都可以在其他平台的jvm上运行. 所以说class文件也没有那么神秘, 它的结构相对来说非常的固定. 每个class文件描述了一个单独的java类或接口.

 

所有类文件中的信息都以下面四种基本类型存储:

u1 a single unsigned byte
u2 two unsigned bytes
u4 four unsigned bytes
u8 eight unsigned bytes

 

class文件的主要组成部分, 按照出现的顺序, 如下面列表所示:

Type Name Count
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 attributes_count 1
attribute_info attributes attributes_count

魔术数字(Magic): 每个class文件的前四个字节都是这样的一个魔术数字:0xCAFEBABE,(Cafe baby, 明白了吧), 如果一个class文件不是以这四个字节开头, 那就不是一个合法的class文件, 这样很容易排除了粗制伪造的class.

大小版本号(minor_version and major_version): 大版本号和小版本号. 因为java技术在不断的进步发展, 许多新的特性会被加入到class文件中, 所以某个特定的jvm只能处理一定版本下的class, 同时会拒绝它不能处理的class.

常量池大小和常量池(constant_pool_count and constant_pool): 常量池包含了类或接口要引用到的常量, 包括字面量, final变量, 类名, 方法名,等等. 通常, 一个常量又需要通过下标引用到其他常量. 每个常量的开头都会有一个标志, 表明该常量的类型. 当虚拟机读到这个标志时, 就知道常量的具体类型了.一下列出这些常量的标志:

Entry Type Tag Value Description
CONSTANT_Utf8 1 A UTF-8 encoded Unicode string
CONSTANT_Integer 3 An int literal value
CONSTANT_Float 4 A float literal value
CONSTANT_Long 5 A long literal value
CONSTANT_Double 6 A double literal value
CONSTANT_Class 7 A symbolic reference to a class or interface
CONSTANT_String 8 A String literal value
CONSTANT_Fieldref 9 A symbolic reference to a field
CONSTANT_Methodref 10 A symbolic reference to a method declared in a class
CONSTANT_InterfaceMethodref 11 A symbolic reference to a method declared in an interface
CONSTANT_NameAndType 12 Part of a symbolic reference to a field or method

常量池在动态链接中扮演重要角色, 除了上面的常量以外, 还有三种符号引用: 类或接口全名, 字段名和描述符, 方法名和描述符. 所有这些链接以符号的形式存在, 当jvm执行时, 需将这些符号引用替换成实际地址.

访问标志(Access Tag): 紧跟在常量池后面的两字节就是该类或接口的访问标志. 它可以是[ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_INTERFACE, ACC_ABSTRACT]的子集.

类名(this_class): 接下来两字节保存一个常量池的索引(因为类名是作为常量保存在常量池中的), 指向常量池中一个CONSTANT_CLASS_INFO.

父类(super_class): 跟随在类名后面, 也是一个指向常量池中的一个的索引. 当this_class为java.lang.Object时, super_class为0.

接口数量和接口(interfaces_count and interfaces): interfaces中以数组的形式保存一些常量池的索引.

字段数量和字段(fields_count and fields): 在field_count后面紧接着是以表格field_info的形式保存了字段信息. 字段和字段数量都只是本类或接口申明的,而不管父类. field_info的每行包含了字段名, 类型描述符, 修饰符,  如果字段申明为final, 则保存它在的常量池中的引用.

方法数量和方法(methods_count and methods): 结构同上. 在methods_count后面紧随method_info表. method_info表中包含了方法名, 修饰符, 返回类型, 参数类型, 非abstract方法还包含该方法的栈的字长, exceptions列表, 字节码序列.

属性数量和属性(attributes_count and attributes): 结构同上, 在attributes_count后面紧随attribute_info表. 属性来自不同的地方, 有一些来自虚拟机规范, 还有一些是实现者自己创建的. 但java虚拟机应该能够忽略那些它不认识的属性.