JVM - class 文件解析

Java虚拟机规范定义了u1,u2,u4是那种类型表示1,2,4个字节无符号整数,分别对应go的uint8,uint16,uint32;相同类型的多条数据一般按表(table)的形式存储在class文件中,表由表头和表项(item)构成,表头是u2或u4整数; 假设表头是n,后面就紧跟着n个表项数据

 

Java 虚拟机中的class文件结构如下

ClassFile {
	u4 		magic;
	u2 		minor_version;
	u2 		major_version;
	u2 		constant_pool_count;
	cp_info 	constant_pool[constant_pool_count-1];
	u2 		access_flags;
	u2 		this_class;
	u2 		super_class;
	u2 		interfaces_count;
	u2 		interfaces[interfaces_count];
	u2 		fields_count;
	field_info 	fields[fields_count];
	u2 		methods_count;
	method_info 	methods[methods_count];
	u2 		attributes_count;
	attribute_info 	attributes[attributes_count];
}

字段说明

magic

魔数,class文件的前4个字节,0XCAFEBABE,用来分辨文件是否为class文件

minor_version 和 major_version

class 文件下面的 4个字节表示了主次版本号,如果class版本号超出了java虚拟机所能处理的范围,虚拟机将不会处理该class文件

constant_pool_count 和 constant_pool

版本号后面是常量池,包含了与文件中类和接口相关的常量;常量池中存储了诸如文字字符串,final变量值、类名和方法名的常量。Java虚拟机把常量池组织为入口列表的形式。在实际列表constant_pool之前,是入口在列表着哦功能的计数constant_pool_count。

​ 常量池中的许多入口都指向其他的常量池入口,而且class文件中紧随着常量池的许多条目也会指向常量池中的入口。在整个class文件中,指示常量池入口在常量池列表中位置的整数索引都指向这些常量池入口。列表着哦功能的第一项索引值为1,第二项索引值为2,依此类推。尽管constant_pool列表中没有索引值为0的入口,但缺失的这一入口也被constant_pool_count计数在内。例如,当constant_pool中一14项(索引值从1到14时),constant_pool_count的值为15.

​ 每个常量池入口都从一个长度为一个字节的标识开始,这个标识指出了列表中该位置的常量类型。一旦java虚拟机获取并解析这个标识,java虚拟机就会知道在标识后的常量类型是什么。

​ 常量池标识

表中的每个标识都有一个相对应的表,表名通过在标识名后加上“_info”后缀来产生。例如,对应于CONSTANT_Class标识的表名为CONSTANT_Class_info,表名为CONSTANT_Utf8_info的表中存储着Unicode字符串的压缩形式。

​ 在动态链接的Java程序中,常量池充当了十分重要的角色。除了字面常量(或者说直接量)值以外,常量池还可以容纳下面几种符号引用:

(1)、类和接口的全限定名;

(2)、字段的名称和描述符;

(3)、方法的名称和描述符;

字段是类或接口的实例变量或者类变量。字段的描述符是一个指示字段的类型的字符串。方法的描述符也是一个字符串,该字符串指示方法的返回值和参数的数量、顺序和类型。在运行时,Java虚拟机使用常量池的全限定名、方法和字段的描述符,把当前类或接口中的代码与其他接口中的代码链接起来。由于class文件并不包含其内部组建最终内存布局的信息,因此类、字段和方法并不能被class文件中的字节码直接引用。Java虚拟机从常量池获得符号引用,然后在运行时解析引用项的实际地址。例如,用来调用方法的字节码指令把一个符号引用的常量池索引传给所调用的方法

access_flags

紧接着常量池后面的 2字节,表示了文件中定义的累活接口的几段信息

所有未使用的位都必须由编译器设置为0,而且Java虚拟机必须忽略它

this_class

2字节,是对常量池的索引.在this_class位置的常量池入口必须为CONSTANT_Class_info表。该表由两个部分组成——标签和name_index。标签部分是一个具有CONSTANT_Class值的常量,在name_index位置的常量池入口为一个包含子类或接口全限定名的CONSTANT_Utf8_info表。

​ this_class项提供了一个如何使用常量池的范例。对于它自身来说,this_class项只是一个指向常量池的索引。当java虚拟机在this_class位置查阅常量池的入口的时候,它会发现一个通过把自己的标签设为CONSTANT_Class来识别自身的项,Java虚拟机知道,在CONSTANT_Class_info入口中,标签的后面总是会有一个名为name_index的、指向常量池的索引,于是虚拟机在name_index位置查找常量池入口,在这个位置,Java虚拟机应该能找到一个容纳了类或者接口全限定名的CONSTANT_Utf8_info入口

super_class

2个字节,里面保存的是该类超类的常量池索引,Object的super_class是0,接口的super_class是java.lang.Object

interfaces_count 和 interfaces

interfaces_count 在文件中由该类直接实现或由接口所扩展的父接口的数量,interfaces 是一个数组,包含了对每个由该类或者接口直接实现的父接口的常量池索引.每个父接口都使用一个常量池中的CONSTANT_Class_info入口来描述,该CONSTANT_Class_info入口指向接口的全限定名。这个数组只容纳哪些直接出现在类声明的implements子句或者接口声明的extends子句中的父接口。超类按照在implements子句和extends子句中出现的顺序(从左到右)在这个数组中显现

fields_count和fields

在class文件中,紧接在interfaces后面的是对在该类或者接口中声明的字段的描述。首先是名为fields_count的计数,它是类变量和实例变量的字段的数量总和。在这个计数后面的是不同长度的field_info表的序列(fields_count指出了序列中有多少个field_info表)。只有在文件中由类或者接口声明了的字段才能在fields列表中列出。在fields列表中,不列出从超类或者父接口继承而来的字段。另一方面,fields列表可能会包含在对应的java源文件中没有叙述的字段,这时因为java编译器可能会在编译时向类或接口添加字段。例如,对于一个内部类的fields列表来说,为了保持对外围类实例的引用,java编译器会为每个外围类实例添加实例变量。源代码中并没有叙述任何在fields列表中的字段,它们是被java编译器在编译时添加进去的,这些字段使用synthetic属性标识。

​ 每个field_info表都展示了一个字段的信息。此表包含了字段的名字、描述符和修饰符。如果该字段被声明为final、field_info表还会展示其常量值。这样的信息有些放在field_info表中,有些则放在由field_info表所指向的常量池中

methods_count和methods

在class文件中,紧接着fields后面的是对在该类或者接口中所声明的方法的描述。首先是名为methods_count的计数,它是一个双字节长度的对于该类或者接口中声明的所有方法的总计数。这个总计数只包括在该类或者接口中显式定义的方法(从超类或者父接口中继承来的方法不被计入)。在methods_count后面的是方法本身,它在一个method_info表的列表中进行了阐述(methods_count指出了列表中有多少个method_info表)。

​ method_info表中包含了与方法相关的一些信息,包括方法名和描述符(方法的返回值类型和参数类型)。如果方法既不是抽象的,又不是本地的,那么method_info表就包含局部变量所需的栈空间长度、为方法所捕获的异常表、字节吗序列以及可选的行数和局部变量标。如果方法能够抛出任何已验证的异常,那么method_info表就会包括一个关于这些已验证异常的列表

 

attributes_count和attributes

​ class文件中最后的部分是属性(attribute),它给出了在该文件中类或者接口所定义的属性的基本信息。属性部分由attriutes_count开始,attributes_count是指出现在后续attributes列表中的attribute_info表的数量总和。每个attribute_info的第一项是指向常量池中CONSTANT_Utf8_info表的索引,该表给出了属性的名称。

​ 属性有许多种。java虚拟机规范定义了几种属性,但任何人都可以创建他们自己的属性种类(通过特定的规则),并且把它们置于class文件中。java虚拟机实现必须忽略任何不能识别的属性。属性出现在class文件中的多处,而不仅仅在顶层ClassFile表的attributes项中出现。出现在ClasssFile表中的属性主要给出了与文件中所定义的类和接口相关的信息;出现在field_info表中的属性主要给出了与字段相关的信息;出现在method_info表中的属性主要给出了方法相关的信息。

其中java虚拟机实现定义了两种属性:SourceCode和InnerClasses

你可能感兴趣的:(JVM,Java)