Java虚拟机——类文件结构

6 类文件结构

  • 各种不同平台的Java虚拟机,以及所有平台都统一支持的程序存储格式——字节码是构成平台无关性的基石。
  • Class文件中包含了Java虚拟机指令集、符号表以及若干其他辅助信息。这些元数据信息可以被JVM运行时环境读取和使用。
  1. 使用Java编译器可以把Java代码编译为 存储字节码的 Class文件。
  2. 使用JRuby等其他语言的编译器一样可以把它们的源程序代码编译成 Class文件。
  • Java语言中的各种语法、关键字、常量变量和运算符号的语义最终都会由多条字节码指令组合来表达。 一些Java语言没有的语言特性在字节码中也可以有效表达出来。
    Java虚拟机——类文件结构_第1张图片

6.1 Class类文件的结构

  • Class文件是一组以字节为基础的二进制流,各个数据项目严格按照顺序紧凑地排列在文件中,中间没添加任何分割符。
  • 根据*,Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型。
  1. 无符号数:属于基本的数据类型,以u1,u2,u4,u8表示1个字节、2个字节…的无符号数。 无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
  2. :是由多个无符号数或其他表作为数据项构成的复合数据类型。所有表的命名都习惯性地以"_info"结尾。 表用于描述有层次关系的复合结构的数据,整个Class文件本质上也可以视作是一张表。
  • Class的结构不像XML等描述语言,它没有任何分隔符号,所以各个数据项无论是顺序还是数量、哪个字节代表什么含义,长度是多少,先后顺序如何,全都不允许改变。

任何一个Class文件都对应着唯一的一个类或接口的定义信息。

6.1.1 魔数与Class文件的版本

  • 每个Class文件的头4个字节被称为魔数。

  • 它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。

  • 魔数不仅是Class文件,很多文件格式标准中都使用魔数来进行身份识别的习惯,例如GIF或者JPEG等的文件头。Class文件的魔数值为0xCAFEBABE

  • 紧接着魔数的4个字节存储的是Class文件的版本号。

  • 第5和第6字节是次版本号

  • 第7和第8个字节是主版本号

  • Java的版本号从45开始,JDK1.1之后每个JDK大版本发布 主版本号向上加1。

  • Java虚拟机会拒绝执行超过其版本号的Class文件。

6.1.2 常量池

  • 跟着主、次版本之后的是常量池入口。 常量池可以比喻为Class文件里的资源仓库。
  • 它是Class文件结构中与其他项目关联最多的数据,通常也是占用Class文件空间最大的数据项目之一。
  • 常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项u2类型的数据,代表常量池容量计数值。

存放两大类常量

  • 字面量和符号引用

  • 字面量:比较接近于Java语言层面的常量概念。如文本字符串、被声明为final的常量值等。

  • 符号引用:属于编译原理方面的概念

  • 常量池中每一项常量都是一个表,截至JDK13,常量表中已经有了17种不同类型的常量。

  • 不同常量类型有着各自完全独立的数据结构,并且它们在常量池中占据的字节大小也是不一样的
    Java虚拟机——类文件结构_第2张图片

6.1.3 访问标志

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

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

  • 类索引、父索引都是一个u2类型的数据。
  • 接口索引集合是一组u2类型的数据的集合。
  • Class文件中由这三项数据来确定该类型的继承关系。
  1. 类索引:用于确定这个类的全限定名
  2. 父类索引:用于确定这个类的父类的全限定名。 (Java中除了java.lang.Object以外,所有Java类的父类索引都不为0)
  3. 接口索引集合:描述了这个类实现了哪些接口
  • 类索引、父类索引和接口索引集合按照顺序排列在访问标志之后。

6.1.5 字段表集合

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

  • 包括类级变量以及实例级变量,但是不包括在方法内部声明的局部变量。

  • 字段表集合主要包含下面4部分内容

  1. 访问标志:表示当前字段的访问权限和特征。(public , private , static 等)
  2. 名称索引:对常量池项的引用,代表着字段的简单名称。(简单名称就是指没有类型和参数修饰的方法或者字段名称,例如这个类中的imc()方法的名称就是imc)
  3. 描述符索引:这也是对常量池项的引用。作用是用来描述字段的数据类型、方法的参数列表和返回值。
  4. 属性表集合:用于存储一些额外的信息,例如ConstantValue属性、Synthetic属性等。
    Java虚拟机——类文件结构_第3张图片

6.1.6 方法表集合

  • Class文件存储格式中对方法的描述与对字段的描述采用了几乎完全一致的方式。

  • 方法表结构如同字段表一样:依次包括访问标志、名称索引、描述符索引、属性表集合几项。

  • 当然方法表的访问标志中也相应增加了 synchronized 、 native 、strictfp 和 abstract等关键字。

  • 方法中的代码,经过Javac编译器编译成字节码指令之后,存放在方法属性表集合中的一个名为"Code"的属性里面。
    Java虚拟机——类文件结构_第4张图片

6.1.7 属性表集合

  • Class文件、字段表、方法表都可以携带自己的属性表集合,以描述某些场景专有的特性。
  • 属性表用来描述类文件的附加信息

Code属性

  • Java程序方法体里面的代码经过Javac编译器处理之后,最终变为字节码指令存储在Code属性内。
  • Code属性出现在方法表的属性集合中。当然不是所有方法表都存在这个属性,譬如接口或者抽象类中的方法就不存在Code属性。

Exceptions属性

  • Exceptions属性是在方法表中和Code属性平级的一项属性。
  • Exceptions属性的作用是列举出方法中可能抛出的受查异常。(也就是方法描述时 throws关键字后面列举的异常)

LineNumberTable属性

  • LineNumberTable属性用于描述 Java源码行号 与 字节码行号 之间对应的关系。
  • 它不是运行时必须的属性,但是默认会生成到Class文件之中。
  • 如果没有,对程序的最主要影响就是抛出异常时,堆栈中不会显示出错的行号,并且在调试程序的时候,无法按照源码行来设置断点。

LocalVariableTable属性和LocalVariableTypeTable属性

  • LocalVariableTable属性用于描述栈帧中 局部变量表 的变量与Java源码中定义的变量之间的关系。
  • 它会默认生成
  • 如果没有生成这项属性,最大的影响就是当其他人引用这个方法的时候,所有的参数名称都会丢失。 譬如IDE会使用诸如arg0、arg1之类的占位符代替原有的参数名。

SourceFile及ScourceDebugExtension属性

  • SouceFile属性用于记录生成这个Class文件的源码名称。
  • 在Java中,大多数的类来说,类名和文件名是一致的。当然内部类这种特殊情况除外。
  • 不生成这项属性的话,抛出异常时,堆栈中将不会显示出错代码所属的文件名。
  • 为了方便在编译器和动态生成的Class中加入供程序员使用的自定义内容,在JDK5中,新增加了SourceDebugExtension属性来存储额外的代码调试信息。

ConstantValue属性

  • ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。
  • 只有被static关键字修饰的变量才可以使用这项属性。
  • 目前Oracle公司使用的Javac编译器,选择的是如果同时使用final和static关键字来修饰一个变量,并且这个变量的数据类型是基本数据类型后者java.lang.String的话,将会生成ConstantValue属性来进行初始化。

InnerClasses属性

  • InnerClasses属性用来记录内部类与宿主之间的关联。
  • 如果一个类包含了内部类,编译器将会它和它所包含的内部类生成InnerClasses属性

Deprecated及Synthetic属性

  • Deprecated属性用来标记某个类、字段或者方法已经被程序作者定义为不再推荐使用,在代码中可以通过@deprecated注解来设置。
  • Synthetic属性代表此字段或者方法不是由Java源码直接产生的,而是由编译器自己添加的。

Signature属性

  • 指定了该类或者接口的泛型签名信息。
  • Java语言的泛型采用的是擦除法实现的伪泛型,字节码(Code属性)中所有的泛型信息编译在编译之后都通通被擦除掉。

BootstrapMethods属性

  • 它是一个复杂的变长属性,位于类文件的属性表中。
  • 这个属性用于保存invokedynamic指令引用的引导方法限定符。

MethodParameters属性

  • MethodParameters是用在方法表中的变成属性。用于记录方法的各个形参名称和信息。
  • 为了解决程序的传播和二次复用的不便等问题,JDK8中加入了这个属性。

运行时注解相关属性

  • 为了存储源码中注解信息,Class文件同步增加了很多注解相关属性。

你可能感兴趣的:(Java虚拟机,java,开发语言)