0 概述
这实际上是《The Java® Virtual Machine Specification - Java SE 8 Edition》中第四章内容(The class File Format)的部分翻译。主要目的是整理阅读笔记,让我自己看得明白,在这个前提下,尽量让别人看得明白,如果读者觉得我写得很混乱,还请自行阅读原文,不便之处敬请见谅。如有错误,还请指正(拉到页面底部点击『联系我』就可以发邮件给我)。
每一个class文件都包含了一个单独的class或者interface的定义。尽管一个class或者interface并不是有一个以文件形式存在的外部表达,但是下面还是通俗地将class或interface的任何有效表达称为类文件格式(the class file format)。
一个类文件由一个8位字节流
组成。 所有的16位,32位和64位量分别通过读取2、4、8个连续的8位字节来构造。 多字节数据项总是以big-endian顺序
存储,其中高字节排在第一位。 在Java SE平台中,此格式由接口java.io.DataInput
和java.io.DataOutput
以及类如java.io.DataInputStream
和java.io.DataOutputStream
支持。
本章定义了自己的一组表示类文件数据的数据类型:
- 类型
u1
,u2
和u4
分别表示一个无符号的一个,两个或四个字节数量(即1 byte、2 byte和4 byte)。
在Java SE平台中,这些类型可以通过接口java.io.DataInput
的readUnsignedByte
,readUnsignedShort
和readInt
等方法读取。
1 ClassFile
结构
一个class文件包含一个单独的ClassFile
结构:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
ClassFile
结构中的各个item
含义如下:
1.1 magic
魔法数字,用于标识class文件格式,固定为0xCAFEBABE
1.2 minor_version, major_version
minor_version和major_version组合到一起,决定了class文件格式的版本。
假设class文件的major_version为$M$,minor_version为$m$,那么我们将class文件格式的版本记为$M.m$,因此,这个版本号可以按字典序排序,例如$1.5<2.0<2.1$。
一个JVM的实现可以支持的class文件格式版本记为$v$,当且仅当$v$落在连续区间 $M_i.0 \leq v \leq M_j.m$ 时成立。
JVM实现所遵循的Java SE平台的release level决定了其支持范围。
例如
- Oracle对JDK release 1.0.2的JVM实现支持的class文件格式版本为$[45.0, 45.3]$
- JDK release 1.1.* 支持$[45.0, 45.65535]$
- 对于$k \geq 2$,JDK release $1.k$ 支持范围为$[45.0, 44+k.0]$
1.3 constant_pool_count
constant_pool[]
中的条目数量+1
1.4 constant_pool[]
constant_pool是一个结构表,表示ClassFile
结构及其子结构中引用的各种
- 字符串常量
- 类和接口名
- 字段名
- 其他常量。
每个constant_pool表项的格式由第一个“标记(tag)”字节表示。
constant_pool表下标的取值范围是$[1,\ constant\_pool\_count)$
1.5 access_flags
access_flags 项的值是用于表示对此类或接口的访问权限和属性的标志的掩码。
16bit
Flag Name | Value | 二进制 | 十进制表示 | Interpretation |
---|---|---|---|---|
ACC_PUBLIC | 0x0001 | 00000000 00000001 | 1 | 声明public |
ACC_FINAL | 0x0010 | 00000000 00010000 | 16 | 声明final |
ACC_SUPER | 0x0020 | 00000000 00100000 | 32 | 当调用invokespecial 指令时,特殊对待父类方法 |
ACC_INTERFACE | 0x0200 | 00000010 00000000 | 512 | 指明这是interface 而非class |
ACC_ABSTRACT | 0x0400 | 00000100 00000000 | 1024 | 声明abstract |
ACC_SYNTHETIC | 0x1000 | 00010000 00000000 | 4096 | 声明synthetic,在源码中不存在 |
ACC_ANNOTATION | 0x2000 | 00100000 00000000 | 8192 | 指明这是注解 |
ACC_ENUM | 0x4000 | 01000000 00000000 | 16384 | 指明这是enum |
1.6 this_class
this_class
的值必须是constant_pool[]
的有效下标。constant_pool[this_class]
必须为CONSTANT_Class_info
结构,表示了在此class文件中定义的类或者接口。
1 | CONSTANT_Class_info { |
1.7 super_class
super_class
的值也必须是constant_pool[]
的有效下标。
对于类来说,
- 如果此值不是0, 则
constant_pool[super_class]
必须为CONSTANT_Class_info
结构,表示了此class文件定义的类的直接父类。 - 如果此值为0,则此class文件必须表示
Object
类,这是唯一没有直接父类的类或者接口。
对于接口来说,没有规定。
1.8 interfaces_count
指明了此类的直接父接口的数量。
1.9 interfaces[]
里面的每个值都必须是constant_pool[]
的有效下标。所指向的constant_pool
中的条目必须为CONSTANT_Class_info
结构,指明了其直接父接口。
1.10 fileds_count
给出fields[]
中field_info
结构的数量。field_info
结构表示了此类或接口所声明的所有field,包括class variables和instance virables(也就是静态变量和实例变量)。
1.11 fields[]
所有值都必须为field_info
结构,给出对变量的完整描述。只包含由此类或者接口声明的变量,不包括继承而来的。
1 | field_info { |
1.12 methods_count
给出methods[]
中method_info
结构的数量
1.13 methods[]
所有值都必须为method_info
结构。
如果ACC_NATIVE
和ACC_ABSTRACT
标志都没有在method_info
里的access_flags
中设置,实现此方法的JVM指令也要提供。
methods[]
表达了此class or intreface声明的所有方法,包括实例方法、类方法、实例初始化方法和class or interface 初始化方法,不包括继承而来的方法。
1 | method_info { |
1.14 attributes_count
size of attributes[]
1.15 attributes[]
1 | attribute_info { |
2 The Internal Form of Names
2.1 Binary Class and Interface Names
class文件结构中,class和interface的名字总是以一种全限定(fully qualified)的形式表达的,被称为binary names
(JLS §13.1)。这些名字总是表达为CONSTANT_Utf8_info
结构。
类和接口名称是从那些CONSTANT_NameAndType_info
结构中引用的, 它们的名称是其描述符的一部分, 并且来自所有CONSTANT_Class_info
结构
由于历史原因, 类文件结构中出现的binary names的语法不同于JLS §13.1中记录的二进制名称的语法。标识符(identifiers)构成了binary names,通常用ASCII码(.
)分割各个标识符,在这种内部形式中,替换为了ASCII码(/
)。标识符本身必须是未限定的名称(unqualified names)。
2.2 Unqualified Names 未限定的名称
方法、field、本地变量和形式参数的名称都存储为unqualified names
。
一个unqualified names
必须包含至少一个Unicode码(Unicode code point),并且不能包含以下ASCII字符:.
, ;
, [
, /
(即,句号、分号、左方括号和斜线)。
方法名的约束还要加上不能出现ASCII字符<
、>
(即左尖括号和右尖括号),例外方法为
<init>
<clinit>
3 Descriptors 描述符
描述符是用于表示field或者方法的字符串。
3.1 Grammer Notaion 语法符号
(先定义用于描述描述符构成的语法):
描述符是使用语法指定的。语法是一组生产、 描述字符序列如何形成各种语法正确的描述符。
- 语法的终端符号以固定宽度字体显示。
- 非终结名称符号以斜体类型显示。
- 非终结名称的定义由定义的非终结名称的名称引入,后跟冒号。
- 非终结名称的一个或多个可选定义随后将跟随后续行。
- 生产右侧的语法 {x} 表示 x 的零个或多个匹配项。
- 生产右侧的短语 (one of) 表示以下行或行上的每个终端符号都是可选的定义。
3.2 Field Descriptors
field descriptors的说明:
FieldType term | Type | Interpretation |
---|---|---|
B | byte | signed byte |
C | char | Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16 |
D | double | double-precision floating-point value |
F | float | single-precision floating-point value |
I | int | integer |
J | long | long integer |
L ClassName ; | reference | an instance of class ClassName |
S | short | signed short |
Z | boolean | true or false |
[ | reference | one array dimension |
例子:
- 一个Object实例表示为:
Ljava/lang/Object;
- 一个多维数组
double[][][]
表示为[[[D
3.3 Method Descriptors 方法描述符
例子:
1
1
Object m(int i, double d, Thread t) {...}
描述符为:
(IDLjava/lang/Thread;)Ljava/lang/Object;
4 The Constant Pool
Java 虚拟机指令不依赖于类、接口、类实例或数组的运行时布局。相反, 指令指的是 constant_pool 表中的符号信息。
constant_pool
中的所有条目都具有以下的一般结构:
1 | cp_info { |
其中,tag
用于指示cp_info
条目的类型,info
数组的内容随tag
的值而变化。每个标记字节必须后跟两个或多个字节, 以提供有关特定常量的信息。tag
的取值范围如下:
Constant Type | Value |
---|---|
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
4.1 CONSTANT_Class_info
1 | CONSTANT_Class_info { |
tag
的值为7
,表示这是一个CONSTANT_Class
,name_index
必须是constant_pool
的有效下标,指向的必须是一个CONSTANT_Utf8_info
结构。也就是说指向了常量池中表示类名的字符串常量。类名表达为internal form
,例如:
int[][]
表达为[[I
Thread[]
表达为[Ljava/lang/Thread;
4.2 CONSTANT_Fieldref_info, CONSTANT_Methodref_info和CONSTANT_InterfaceMethodref_info
1 | CONSTANT_Fieldref_info { |
4.3 CONSTANT_String_info
1 | CONSTANT_String_info { |
- string_index
必须是constant_pool
的有效下标,必须指向CONSTANT_Utf8_info
结构。
4.4 CONSTANT_Integer_info和CONSTANT_Float_info结构
1 | CONSTANT_Integer_info { |
- bytes
表达实际值
4.5 CONSTANT_Long_info和CONSTANT_Double_info结构
1 | CONSTANT_Long_info { |
4.6 CONSTANT_NameAndType_info
1 | CONSTANT_NameAndType_info { |
4.7 CONSTANT_Utf8_info
1 | CONSTANT_Utf8_info { |
4.8 CONSTANT_MethodHandle_info
1 | CONSTANT_MethodHandle_info { |
reference_kind
The value of the reference_kind item must be in the range 1 to 9. The value denotes the kind of this method handle, which characterizes its bytecode behavior (§5.4.3.5)(Chapter 5: Loading, Linking, and Initializing).
- reference_index
必须是constant_pool
的有效下标,指向的结构根据reference_kind
的不同而有不同的要求:- 如果
reference_kind
为1、2、3、4,必须指向CONSTANT_Fieldref_info
结构 - 如果
reference_kind
为5、8,必须指向CONSTANT_Methodref_info
,而此结构表达了类的方法或构造器 - 如果
reference_kind
为6、7,然后- 如果class文件的版本号小于52.0,必须指向
CONSTANT_Methodref_info
结构,此结构表示类的方法; - 如果class文件的版本号大于或等于52.0,则必须指向
CONSTANT_Methodref_info
结构或者CONSTANT_InterfaceMethodref_info
结构,表达了类或接口的方法;
- 如果class文件的版本号小于52.0,必须指向
- 如果
reference_kind
为9,必须指向CONSTANT_InterfaceMethodref_info
结构,表达了接口方法。
- 如果
4.9 CONSTANT_MethodType_info
1 | CONSTANT_MethodType_info { |
4.10 CONSTANT_InvokeDynamic_info
1 | CONSTANT_InvokeDynamic_info { |
- bootstrap_method_attr_index
The value of the bootstrap_method_attr_index item must be a valid index into the bootstrap_methods array of the bootstrap method table (§4.7.23) of this class file.
5 Fields
1 | field_info { |
- attributes[]
里面的每个item都必须是attribute_info
结构
6 Methods
1 | method_info { |
- attributes[]
里面的每个item都必须是attribute_info
结构
7 Attributes
1
2
3
4
5attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
The Code Attribute
1 | Code_attribute { |
- max_stack
给出在方法执行的任意地方操作数栈的最大深度 - max_locals
给出方法调用时本地方法表的最大容量 - code[]
给出了实现方法的JVM代码的实际字节(byte)。
The BootstrapMethods Attribute
1 | BootstrapMethods_attribute { |
- bootstrap_methods[]
- bootstrap_method_ref
其值必须是constant_pool
中的有效下标,指向的必须是CONSTANT_MethodHandle_info
结构
- bootstrap_method_ref
8 Format Checking
(TBD)
9 Constraints on Java Virtual Machine Code
(TBD)
10 Verification of class Files
(TBD)
10.1 Verification by Type Checking
10.2 Verification by Type Inference
11 JVM的限制
ClassFile
结构中16-bit的constant_pool_count
限制了per-class或者per-interface的常量池最多只有65535个条目(entries)。这对单个类或接口的总体复杂性起到了内部限制作用。ClassFile
结构中的fields_count
限制了一个class or interface能声明的field
数量不能超过65535。(不包括继承的)- 方法数量限制同上。(
methods_count
) - 直接父接口的数量限制同上(
interfaces_count
) - 方法调用时创建的帧里面,本地变量表中的本地变量数量最多为65535,由
Code
attribute中的max_locals
item所限制,以及由JVM指令集的16-bit本地变量索引所限制。
其中,long
和double
类型视为两个本地变量 Code
attribute中的max_stack
item限制了frame中的操作数栈的大小为65535
其中,long
和double
类型的操作数视为两个单元method descriptor
的定义限制了方法参数的数量最多为255
其中,实例方法的this
占了一个单元,long
和double
类型的会占两个单元- field和方法的名称、field和方法的descriptor以及其他string常量值(包括被
ConstantValue
attribute引用的)最多为65535个byte,由CONSTANT_Utf8_info
结构中的16-bit无符号length
item所限制 数组的维度最多为255,由
multianewarray
指令中的opcodedimensions
的大小所限制The number of dimensions in an array is limited to 255 by the size of the dimensions opcode of the multianewarray instruction and by the constraints imposed on the multianewarray, anewarray, and newarray instructions