类文件结构

类文件结构

第一节 概述

  记得在第一节计算机程序课上老师就讲过:“计算机只认识0和1,所以我们写的程序需要被编译器翻译成由0和1构成的二进制格式才能被计算机执行”。10多年的时间过去了,今天的计算机仍然只能识别0和1,但由于最近10年内虚拟机及建立在虚拟机之上的大量程序语言如雨后春笋般出现并蓬勃发展,将我们编写的程序编译成二进制本地机器码(Native Code)已不再是唯一的选择,越来越多的程序语言选择了与操作系统和机器指令集无关的、平台中立的格式作为程序编译后的存储格式。


第二节 无关性的基石

  如果计算机的CPU指令集就只有x86一种,操作系统就只有Windows一种,那也许就不会有Java语言的出现。Java在刚刚诞生之时曾经提出过一个非常著名的宣传口号:“一次编写,到处运行(Write Once, Run Anywhere)”,这句话充分表达了软件开发人员对冲破平台界限的渴求。在无时无刻不充满竞争的IT领域,不可能只有Wintel存在,我们也不希望只有Wintel存在,各种不同的硬件体系结构和不同的操作系统定将会长期并存发展。“与平台无关”的理想最终实现在操作系统的应用层上:Sun公司及其他虚拟机提供商发布了许多可以运行在各种不同平台上的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码,从而实现程序的“一次编写,到处运行”。

  各种不同平台的虚拟机与所有平台都统一使用的程序存储格式—字节码(ByteCode)是构成平台无关性的基石,但本节标题中刻意省略了“平台”二字,那是因为作者注意到虚拟机的另外一种中立的特性—如今语言无关性越来越被开发者所重视。到今天为止,或许大部分程序员都还认为Java虚拟机执行Java程序是一件理所当然和天经地义的事情。但在Java发展之初,设计者们就曾经考虑过并实现了让其他语言运行在Java虚拟机之上的可能性,他们在发布规范文档的时候,也刻意把Java的规范拆分成了Java语言规范《The Java Language Specification》及Java虚拟机规范《The Java Virtual Machine Specification》。并且在1997年发布的第一版Java虚拟机规范中就曾经承诺过:“In the future,we will consider bounded extensions to the Java virtual machine to provide better support for other languages”(在未来,我们会对Java虚拟机进行适当的扩展,以便更好地支持其他语言运行于JVM之上),当Java虚拟机发展到JDK 1.7~1.8的时候,JVM设计者通过JSR-292基本兑现了这个承诺

  时至今日,商业机构和开源机构已经在Java语言之外发展出一大批在Java虚拟机之上运行的语言,如Clojure、Groovy、JRuby、Jython、Scala,等等。使用过这些语言的开发者可能还不是非常多,但是听说过的人肯定已经不少,随着时间的推移,谁能保证日后Java虚拟机在语言无关性上的优势不会赶上甚至超越它在平台无关性上的优势呢?

  实现语言无关性的基础仍然是虚拟机和字节码存储格式,使用Java编译器可以把Java代码编译为存储字节码的Class文件,使用JRuby等其他语言的编译器一样可以把程序代码编译成Class文件,虚拟机并不关心Class的来源是什么语言,只要它符合Class文件应有的结构就可以在Java虚拟机中运行,如图6-1所示。

Java虚拟机提供的语言无关性

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


第三节 Class类文件的结构

  类文件结构-Class类文件的结构


第四节 字节码指令简介

  类文件结构-字节码指令简介


第五节 公有设计和私有实现

  Java虚拟机规范描绘了Java虚拟机应有的共同程序存储格式:Class文件格式以及字节码指令集。这些内容与硬件、操作系统及具体的Java虚拟机实现之间是完全独立的,虚拟机实现者可能更愿意把它们看做是程序在各种Java平台实现之间互相安全地交互的手段。

  理解公有设计与私有实现之间的分界线是非常有必要的,Java虚拟机实现必须能够读取 Class文件并精确实现包含在其中的Java虚拟机代码的语义。拿着Java虚拟机规范一成不变地 逐字实现其中要求的内容当然是一种可行的途径,但一个优秀的虚拟机实现,在满足虚拟机 规范的约束下对具体实现做出修改和优化也是完全可行的,并且虚拟机规范中明确鼓励实现 者这样做。只要优化后Class文件依然可以被正确读取,并且包含在其中的语义能得到完整的 保持,那实现者就可以选择任何方式去实现这些语义,虚拟机后台如何处理Class文件完全是 实现者自己的事情,只要它在外部接口上看起来与规范描述的一致即可[1]。

  虚拟机实现者可以使用这种伸缩性来让Java虚拟机获得更高的性能、更低的内存消耗或 者更好的可移植性,选择哪种特性取决于Java虚拟机实现的目标和关注点是什么。虚拟机实 现的方式主要有以下两种:

  • 将输入的Java虚拟机代码在加载或执行时翻译成另外一种虚拟机的指令集。
  • 将输入的Java虚拟机代码在加载或执行时翻译成宿主机CPU的本地指令集(即JIT代码生 成技术)。

  精确定义的虚拟机和目标文件格式不应当对虚拟机实现者的创造性产生太多的限制,Java虚拟机应被设计成可以允许有众多不同的实现,并且各种实现可以在保持兼容性的 同时提供不同的、新的、有趣的解决方案。


第六节 Class文件结构的发展

  Class文件结构自Java虚拟机规范第1版订立以来,已经有十多年的历史。这十多年 间,Java技术体系有了翻天覆地的改变,JDK的版本号已经从1.0提升到了1.7。相对于语言、 API以及Java技术体系中其他方面的变化,Class文件结构一直处于比较稳定的状态,Class文 件的主体结构、字节码指令的语义和数量几乎没有出现过变动[1],所有对Class文件格式的改 进,都集中在向访问标志、属性表这些在设计上就可扩展的数据结构中添加内容。

  如果以《Java虚拟机规范(第2版)》为基准进行比较的话,那么在后续Class文件格式 的发展过程中,访问标志里新加入了ACC_SYNTHETIC、ACC_ANNOTATION、 ACC_ENUM、ACC_BRIDGE、ACC_VARARGS共5个标志。而属性表集合中,在JDK 1.5到 JDK 1.7版本之间一共增加了12项新的属性,这些属性大部分用于支持Java中许多新出现的语 言特性,如枚举、变长参数、泛型、动态注解等。还有一些是为了支持性能改进和调试信 息,譬如JDK 1.6的新类型校验器的StackMapTable属性和对非Java代码调试中用到的 SourceDebugExtension属性。

  Class文件格式所具备的平台中立(不依赖于特定硬件及操作系统)、紧凑、稳定和可扩 展的特点,是Java技术体系实现平台无关、语言无关两项特性的重要支柱。


第七节 总结

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

  本章详细讲解了Class文件结构中的各个组成部分,以及每个部分的定义、数据结构和使 用方法。通过代码清单6-1的Java代码与它的Class文件样例,以实战的方式演示了Class的数 据是如何存储和访问的。从第7章开始,我们将以动态的、运行时的角度去看看字节码流在 虚拟机执行引擎中是怎样被解释执行的。


参考博客和文章书籍等:

《深入理解Java虚拟机》

因博客主等未标明不可引用,若部分内容涉及侵权请及时告知,我会尽快修改和删除相关内容