JVM 类文件结构

本文将介绍类文件的结构。

一、什么是类文件?

类文件,即 Class 文件。

类文件由编译器根据代码编译而成。

类文件可以由 JVM 载入并执行。

类文件与平台无关,其内容只针对通用的执行平台——JVM。类文件和 JVM 一起,实现了 Java 的 “一次编写,处处运行”。

二、类文件结构

1. 整体说明

(1) 二进制流

以字节为基础单位的二进制流。

(2) 紧凑排列

各个数据项目严格按照顺序紧凑排列,中间没有任何分隔符。

(3) 数据类型

包含两种数据类型,如下:

  • 无符号数:可以用于描述数字、索引引用、数量值、字符串值;以 u1、u2、u4、u8 分别代表使用不同数量字节的无符号数
  • 表:由多个无符号数、表构成的复合数据结构,简单来说,一个表由多个元素紧密排列构成,这些数据项可以是无符号数、表;Class 文件本身就可以看作是一个表;表的命名习惯以 _info 结尾

2. 魔数

每个 Class 文件的头 4 个字节被称为魔数,其值固定为 0xCAFEBABE(咖啡宝贝)。

JVM 会读取魔数以确认文件符合要求。

3. 版本号

紧随魔数的 4 个字节存储的是 Class 文件的版本号,其中前两个字节存储次版本,后两个字节存储主版本。

JVM 会读取版本号,并拒绝执行版本超过自身的 Class 文件。

4. 常量池

常量池可以比喻为 Class 文件里的资源仓库。

常量池中常量的数量是不固定,因此在常量池开始的地方会放置一个 u2 类型的数据,其值为常量数。

每一个常量具体在 Class 文件中都是一个表,表的类型共有 17 种,如下:

这些常量可以被分为两大类:

  • 字面量:常量,例如字符串、final 常量等
  • 符号引用:类、接口、字段、方法的符号引用;在类加载或运行时,由 JVM 将符号引用解析为直接引用

5. 访问标志

访问标志由 2 个字节构成,共有 2 x 8 = 16 个标志位。

目前共定义了 9 个标志位,如下:

6. 类索引、父类索引、接口索引集合

其中,

  • 类索引:由一个索引项构成
  • 父类索引:由一个索引项构成
  • 接口索引集合:由多个索引项构成

索引项是一个 u2 类型的数据,指向常量池中一个类型为 CONSTANT_Class_info 的常量。

通过索引项,可以获取到类、父类、实现接口的全限定名。

7. 字段表集合

字段表用于描述类中声明的 field。

字段表结构如下:

  • access_flags:即访问标志;与类的访问标志类似
  • name_index:对常量池项的引用;表示字段的简单名称
  • descriptor_index:对常量池项的引用;表示字段的描述符,即参数类型
  • attributes_count 和 arrtibutes:属性表集合;用于存储一些额外信息

8. 方法表集合

方法表用于描述类中声明的 method。

方法表结构如下:

  • access_flags:即访问标志;与类的访问标志类似

  • name_index:对常量池项的引用;表示方法的简单名称

  • descriptor_index:对常量池项的引用;表示方法的描述符,即返回类型、参数列表

  • attributes_count 和 arrtibutes:属性表集合;用于存储一些额外信息

    属性表集合中的 Code 属性存放着(编译成字节码指令的)方法具体代码

三、字节码指令

1. 概述

字节码指令由两部分组成:

  • 第一部分:操作码;一个字节长度,代表某种特定操作含义的数字
  • 第二部分:操作数集合;操作所需参数,有零至多个

2. 操作

由于操作码仅由一个字节存放,因此操作数量不能操作 256 条。

与数据类型相关的操作如图所示:

另外还有与数据类型无关的操作若干,包括对象创建及访问、操作数栈管理、控制转移、方法调用和返回、异常处理、同步等。

参考

  • 深入理解 Java 虚拟机