Class 类文件结构

Class 文件是一组以 8 位为基础单位的二进制流,各个数据项目严格按照顺序排列在 Class 文件之中,中间没有任何分割符。若数据项占用空间超过 8 位,则按照高位在前 (Big-Endian) 的方式分割成多个 8 位存储。

前言

一本好书,每读一遍都会有不同的感受。写读书笔记,一来是便于平时查阅,毕竟技术书籍都比较厚,不方便随时携带;二来是督促自己多读书,理论与实践结合才能不断提升自己。

【深入理解Java虚拟机】阅读笔记: Class 类文件的结构

Class 文件结构

Class 文件格式采用类似于 C 语言结构体的伪结构来存储数据,包含两种数据类型:无符号数和表

无符号数属于基本的数据类型,以 u1、u2、u4、u8代表不同字节的无符号数

表是有多个无符号数或者其他表作为数据项构成的复合数据类型,一般以 “_info” 结尾

1
2
3
graph TD
R[Class 文件数据类型] --> A(无符号数)
R --> B(表)

Class 文件结构如下:

类型名称数量
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count - 1
u2access_flags1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_count

代码

示例代码如下

1
2
3
4
5
6
public class TestClass {
private int m;
public int inc() {
return m + 1;
}
}

Class 文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
00000000: cafe babe 0000 0034 0013 0a00 0400 0f09  .......4........
00000010: 0003 0010 0700 1107 0012 0100 016d 0100 .............m..
00000020: 0149 0100 063c 696e 6974 3e01 0003 2829 .I...<init>...()
00000030: 5601 0004 436f 6465 0100 0f4c 696e 654e V...Code...LineN
00000040: 756d 6265 7254 6162 6c65 0100 0369 6e63 umberTable...inc
00000050: 0100 0328 2949 0100 0a53 6f75 7263 6546 ...()I...SourceF
00000060: 696c 6501 000e 5465 7374 436c 6173 732e ile...TestClass.
00000070: 6a61 7661 0c00 0700 080c 0005 0006 0100 java............
00000080: 0954 6573 7443 6c61 7373 0100 106a 6176 .TestClass...jav
00000090: 612f 6c61 6e67 2f4f 626a 6563 7400 2100 a/lang/Object.!.
000000a0: 0300 0400 0000 0100 0200 0500 0600 0000 ................
000000b0: 0200 0100 0700 0800 0100 0900 0000 1d00 ................
000000c0: 0100 0100 0000 052a b700 01b1 0000 0001 .......*........
000000d0: 000a 0000 0006 0001 0000 0001 0001 000b ................
000000e0: 000c 0001 0009 0000 001f 0002 0001 0000 ................
000000f0: 0007 2ab4 0002 0460 ac00 0000 0100 0a00 ..*....`........
00000100: 0000 0600 0100 0000 0400 0100 0d00 0000 ................
00000110: 0200 0e0a ....

javap -verbose TestClass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Classfile /Users/gorden5566/test/TestClass.class
Last modified Jan 29, 2018; size 275 bytes
MD5 checksum 61a8d824e15e5ec23a32ebe7e60eeffd
Compiled from "TestClass.java"
public class TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#16 // TestClass.m:I
#3 = Class #17 // TestClass
#4 = Class #18 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 inc
#12 = Utf8 ()I
#13 = Utf8 SourceFile
#14 = Utf8 TestClass.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = NameAndType #5:#6 // m:I
#17 = Utf8 TestClass
#18 = Utf8 java/lang/Object
{
public TestClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0

public int inc();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field m:I
4: iconst_1
5: iadd
6: ireturn
LineNumberTable:
line 4: 0
}
SourceFile: "TestClass.java"

魔数与 Class 文件的版本

1
00000000: cafe babe 0000 0034 0013 0a00 0400 0f09  .......4........

头 4 个字节称为魔数 (Magic Number), 值为 0xCAFEBABE
第 5、6 个字节是次版本号 (Minor Version),第 7、8 个字节是主版本号 (Major Version)。Java 版本号从 45 (Java 1.1,十六进制 2D) 开始,Java 1.8 对应 52 (十六进制 34)

常量池

常量池容量计数从 1 开始,如本例中为十六进制 0x0013 (十进制 19) 表示有 18 项常量
可使用 javap -verbose 命令查看常量池内容

常量池每一项都是一个表,表开始的第一位是一个 u1 类型的标志位,各类型如下:

类型标志描述
CONSTANT_Utf8_info1UTF-8 编码的字符串
CONSTANT_Integer_info3整型字面量
CONSTANT_Float_info4浮点型字面量
CONSTANT_Long_info5长整型字面量
CONSTANT_Double_info6双精度浮点型字面量
CONSTANT_Class_info7类或接口的符号引用
CONSTANT_String_info8字符串类型字面量
CONSTANT_Fieldref_info9字段的符号引用
CONSTANT_Methodref_info10类中方法的符号引用
CONSTANT_InterfaceMethodref_info11接口中方法的符号引用
CONSTANT_NameAndType_info12字段或方法的部分符号引用
CONSTANT_MethodHandle_info15表示方法句柄
CONSTANT_MethodType_info16标识方法类型
CONSTANT_InvokeDynamic_info18表示一个动态方法调用点

访问标志

访问标志大小为 u2,位于常量池之后

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

按顺序排列在访问标志之后

1
000000a0: 0300 0400 0000 0100 0200 0500 0600 0000

类索引为 3,父类索引为 4,接口索引集合为 0。对应常量如下

1
2
3
4
5
#3 = Class              #17            // TestClass
#4 = Class #18 // java/lang/Object

#17 = Utf8 TestClass
#18 = Utf8 java/lang/Object

参考

周志明. 深入理解Java虚拟机