Class文件

无关性

不同平台的Java虚拟机与所有平台都使用统一的程序存储格式字节码(ByteCode)来实现Java的平台无关性。此外,在Java虚拟机上也开发出了许多语言,包括Clojure、Groovy、JRuby、Jython、Scala等。Java虚拟机不和包括Java在内的任何语言绑定,它只与Class文件关联,Class文件中包含了Java虚拟机指令集和符号表以及其他辅助信息,虚拟机并不关心Class来源于何种语言。

Class文件结构

Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件之中,中间没有添加任何分隔符,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据。当遇到需要占用8位字节以上空间的数据项时,会按照高位在前的方式分割成若干个8位字节进行存储。Class文件采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构只有两种数据类型:无符号数和表。

  • 无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表一个字节、两个字节、四个字节和八个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
  • 表是由多个无符号数或者其他表作为数据项构成的符合数据类型,所有表都以“_info”结尾,表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。表内数据描述类的所有属性,数据项顺序、数量和字节序(Big-Endian)都是严格限定的。

魔数与Class文件的版本

每个Class文件开头的u4称为魔数(Magic Number),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。Class文件的魔数为0xCAFEBABE。紧接着魔数的4个字节存储的是Class文件版本号,第5和第6是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version),虚拟机不会执行超过其版本号的Class文件,但向下兼容。

常量池

主版本号之后是常量池入口,常量池是Class文件的资源仓库,它是Class文件结构中与其它项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表类型数据项目。常量池入口有一个u2记录常量池容量,容量为该数减一。常量池主要存放两大类常量:字面量(Literal)和符号引用(Symbolic Reference)。字面量包括文本字符串、final常量等。符号引用包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。

Java代码在编译时,虚拟机加载Class文件进行动态连接,虚拟机运行时,要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址中。常量池中每一项常量都是一个表,表开始的第一位u1是标志位,代表常量属于哪种类型。

使用jdk bin目录的javap可以查看Class文件字节码内容,下图是一个类的字节码,可以清楚的看到版本号、常量池等信息。

Class文件_第1张图片

访问标志(access_flags)

常量池之后是两个字节的访问标志,这个标志用于识别一些类或者接口层次的访问信息,包括这个类是类还是接口(ACC_INTERFACE)、是否是public(ACC_PUBLIC)等。如下图所示(ACC_SUPER在jdk1.0.2之后都为真):
这里写图片描述

类索引(this_class)、父类索引(super_class)与接口索引集合(interfaces)

类索引和父类索引是两个u2,接口索引集合是一个u2的集合,类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,因为可以实现多个接口,这些接口从左到右排列在接口集合中,都排在访问标志之后。

类索引和父类索引指向一个类型为CONSTANT_Class_info的类描述符常量,通过类描述符常量的索引值找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串。接口索引集合第一项为接口计数器。

字段(field_info)表集合

字段表集合用于描述接口或类中声明的变量,字段包括类级变量及实例级变量,但不包括在方法内部声明的局部变量。字段包括的信息有:字段作用域、可变性、数据类型等。字段表的具体结构是:access_flags、name_index、descriptor_index、attributes_count、attributes。

方法表集合

方法表的结构和字段表一样,方法里的代码存放在“Code”属性表里面,如果没有覆盖父类方法,就不会有父类额方法信息。要重载一个方法,要有与原方法不同的特征签名,代码的特征签名是一个方法中各个参数在常量池中的字段符合引用的集合。因为返回值不包含在特征签名中,所以无法只靠返回值不同来让函数重载。字节码的特征签名还包括方法返回值和受查异常表,所以如果两个函数名称和特征签名相同,返回值不同,也可以存在于一个Class文件中,Java代码里就不能了。

属性表(attribute_info)集合

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

属性表中包括Code、ConstantValue、LocalVariableTable等。下图为一个方法表中的属性表:
Class文件_第2张图片

没有参数的方法的args_size=1,这是因为编译器把对this关键字的访问转换为对一个普通方法参数的访问,因此至少有一个指向当前对象的局部变量,只对示例方法有效,如果是static方法就为0。


异常表会为try…catch..finally生成三条异常记录,分别对应三种情况:

  • 如果try语句块出现属于Exception或其子类的异常,转到catch语句块处理;
  • 如果try语句块出现不属于Exception或其异常的子类,转到finally语句块处理;
  • 如果catch语句块未出现任何异常,转到finally语句块处理。

你可能感兴趣的:(JVM学习)