Material Render Phone

Tomxin7

“工欲善其事,必先利其器”,作为Java核心之一的JVM,是一个Java开发者必须要去理解的部分,在校招的时候,走马观花式的过了一遍,很多东西没有深入理解,所以打算再看一遍《深入理解JAVA虚拟机》,把其中重要的部分整理出来,记录在博客之中。

Java类文件结构

Class类文件结构

Class文件是一组以8字节为基础单位的二进制流,当遇到8位字节以上的空间的数据项时,则会按照高位在前的方式进行分割成若干个8位字节进行存储

各个数据项目严格按照顺序紧凑排列在class文件中

中间没有任何分隔符,这使得class文件中存储的内容几乎是全部程序运行的程序。
Java虚拟机规范规定,Class文件格式采用类似C语言结构体的伪结构来存储数据,这种结构只有两种数据类型:无符号数和表。

无符号数:

属于基本数据类型,主要可以用来描述数字、索引符号、数量值或者按照UTF-8编码构成的字符串值,大小使用u1、u2、u4、u8分别表示1字节、2字节、4字节和8字节。

表:

是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有的表都习惯以“_info”结尾。表主要用于描述有层次关系的复合结构的数据,比如方法、字段。需要注意的是class文件是没有分隔符的,所以每个的二进制数据类型都是严格定义的。

在class文件中,主要分为魔数、Class文件的版本号、常量池、访问标志、类索引(还包括父类索引和接口索引集合)、字段表集合、方法表集合、属性表集合。

魔数

  • 每个Class文件的头4个字节称为魔数(Magic Number)
  • 唯一作用是用于确定这个文件是否为一个能被虚拟机接受的Class文件。
  • Class文件魔数的值为16进制数——CAFEBABE。如果一个文件不是以0xCAFEBABE开头,那它就肯定不是Java class文件。
  • 很多文件存储标准中都使用魔数来进行身份识别,譬如图片格式,如gif或jpeg等在文件头中都存有魔数。使用魔术而不是使用扩展名是基于安全性考虑的——扩展名可以随意被改变!!!

Class文件的版本号

紧接着魔数的4个字节是Class文件版本号,版本号又分为:
1. 次版本号(minor_version): 前2字节用于表示次版本号
1. 主版本号(major_version): 后2字节用于表示主版本号。


这个的版本号是随着jdk版本的不同而表示不同的版本范围的。Java的版本号是从45开始的。如果Class文件的版本号超过虚拟机版本,将被拒绝执行。
0X0034(对应十进制的50):JDK1.8
0X0033(对应十进制的50):JDK1.7
0X0032(对应十进制的50):JDK1.6
0X0031(对应十进制的49):JDK1.5
0X0030(对应十进制的48):JDK1.4
0X002F(对应十进制的47):JDK1.3
0X002E(对应十进制的46):JDK1.2

ps:0X表示16进制

常量池

紧接着魔数与版本号之后的是常量池入口.常量池简单理解为class文件的资源从库
- 是Class文件结构中与其它项目关联最多的数据类型
- 是占用Class文件空间最大的数据项目之一
- 是在文件中第一个出现的表类型数据项目

访问标志

常量池之后的数据结构是访问标志(access_flags),这个标志主要用于识别一些类或接口层次的访问信息,主要包括:
- 是否final
- 是否public,否则是private
- 是否是接口
- 是否可用invokespecial字节码指令
- 是否是abstact
- 是否是注解
- 是否是枚举

类索引、父类索引和接口索引集合

  • 类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interfaces)是一组u2类型的数据的集合,class文件中由这三项数据来确定这个类的继承关系.
    类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名.接口索引集合用来描述这个类实现了哪些接口,这些被实现的接口按implements语句后的接口顺序从左到右排列在接口索引集合中.
  • 类索引,父类索引和接口都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的索引值表示,它们各自指向一个类型为CONSTANT_Class_info的类描述常量,通过CONCTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Class_info类型的常量中的全限定名字符串.
  • 对接口索引集合,入口的第一项u2类型的数据为接口计数器(interface_count)表示索引表的容量.如果该类没有实现任何接口,则该计数器值为0,后面接口的索引表不再占用任何字节.

字段表集合

字段表(field_info)用于描述接口或类中声明的变量。

字段(field)包括了类级变量和实例级变量,但不包括方法内部声明的变量。一个字段的信息包括:作用域(public、private、protected修饰符)、是实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主内存读写)、可否序列化(transient修饰符)、字段数据类型(基本数据类型、对象、数组)、字段名称。这些信息中,各个修饰符都是布尔值,要么有,要么没有。

方法表集合

Class文件存储格式中对方法的描述与对字段的描述几乎完全一致。方法表的结构如同字段表一样,一次包括了访问标志、名称索引、描述符索引、属性表集合几项。

属性表集合

Java虚拟机规范第二版中预定义了9项虚拟机应当能识别的属性,包括:Code、ConstantValue、Deprecated、Exceptions、InnerClasses、LineNumberTable、LocalVariableTable、SourceFile、Synthetic。

转载声明:写作不易,商业转载请联系作者获得授权,非商业转载请注明出处,并附上原文链接,感谢!