类文件结构

前言

代码编译的结果从本地机器码转变为字节码,是存储格式发展得一小步,却是编程语言发展的一大步。Java编译器将Java代码编译成字节码,存储在扩展名为class的文件中,我们称它为类文件。

Java语言中的各种变量、关键字和运算符号的语义最终都是由多条字节码命令组合而成的,因此字节码命令所能提供的语义描述能力肯定会比Java语言本身更加强大。因此,有一些Java语言本身无法有效支持的语言特性不代表字节码本身无法有效支持,这也为其他语言实现一些有别于Java的语言特性提供了基础。

类文件的结构

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据,没有空隙存在。当遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分隔成若干个8位字节进行存储。

Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种结构中只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数。它用来描述数字、索引引用、数量值或者按照UTF-8编码构成的字符串值。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用来描述有层次关系的复合结构的数据。
整个Class文件本质上就是一张表,它由下图所示的数据项构成:
在这里插入图片描述
下面分别学习下各个数据项的含义。

魔数

每个Class文件的头4个字节称为魔数,它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。使用魔数而不是扩展名来进行识别主要是基于安全方面的考虑,因为扩展名可以随意改动。

Class文件的版本

紧接着魔数的4个字节存储的是Class文件的版本号:第5个和第6个字节是次版本号,第7个和第8个字节是主版本号。Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布,主版本号向上加1。JDK1.1能支持版本号为45.0 ~ 45.65535的Class文件,无法执行版本号为46.0以上的Class文件,JDK是向下兼容的。

常量池

紧接着主版本号的便是常量池,常量池可以理解为Class文件中的资源仓库,它是Class文件结构中与其他项目关联最多的数据类型。
由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值。
常量池中主要存放两大类常量:字面量和符号引用。
符号引用包括三类常量:

  • 类和接口的全限定名。
  • 字段的名称和描述符。
  • 方法的名称和描述符。

符号引用在虚拟机加载Class文件时进行转换,从而得到真正的内存入口地址(直接引用)。

访问标志

在常量池结束之后,紧接着的两个字节代表访问标志,这个标志用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。

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

Class文件中由这三项数据来确定这个类型的继承关系。类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名。接口索引集合用来描述这个类实现了哪些接口,这些被实现的接口将按implements语句后的接口顺序从左到右排列在接口索引集合中。

字段表集合

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

方法表集合

和字段表类似,用于描述类或者接口中的方法。

属性表集合

Class文件、字段表、方法表都可以携带自己的属性表集合,以用于描述某些场景专有的信息。比如SourceFile属性,用于记录生成这个Class文件的源码文件名称。

字节码指令

类文件加载到内存后,虚拟机需要通过字节码指令来执行。Java 虚拟机的指令(字节码指令)由一个字节长度的、代表着某种特定操作含义的数字(操作码)以及跟随其后的零至多个操作所需参数(操作数)构成。
字节码指令集是一种具有鲜明特点、优劣势都很突出的指令集架构,由于限制了Java虚拟机操作码的长度为一个字节,意味着指令集的操作码总数不能超过256条。

字节码指令集包括:加载和存储指令;运算指令;类型转换指令;对象创建与访问指令;操作数栈管理指令;控制转移指令;方法调用和返回指令;异常处理指令;同步指令等。

公有设计和私有实现

Java虚拟机规范描绘了Java虚拟机应有的共同程序存储格式:Class文件格式以及字节码指令集。这些内容与硬件、操作系统及具体的Java虚拟机实现之间是完全独立的。Java虚拟机实现必须能够读取Class文件并精确实现包含在其中的Java虚拟机代码的语义。一个优秀的虚拟机,在满足虚拟机规范的情况下对具体实现做出修改和优化也是完全可以的,并且虚拟机规范中明确鼓励实现者这样做。
虚拟机实现者可以使用这种伸缩性来让Java虚拟机获得更高的性能、更低的内存消耗或者更好的移植性,选择哪种特性取决于Java虚拟机实现的目标和关注点是什么。

总结

Class文件是Java虚拟机执行引擎的数据入口,也是Java技术体系的基础构成之一。了解Class文件的结构对后面进一步了解虚拟机执行引擎有很重要的意义。

原文:《深入理解JVM》,周志明

展开阅读全文

没有更多推荐了,返回首页