JVM之Class文件分析详解

JVM之Class文件分析详解

逐个字节分析Class文件。了解Class文件内部结构。

原始类文件:

这个类直接拿了知乎的一个类,分析是很简单,只是因为重新画图比较麻烦,不想再做重复的苦力工作(地址(https://zhuanlan.zhihu.com/p/23068093)ps:作者仅仅提供了类和图,并无解析。)

1
2
3
4
5
6
7
8
9
package com.vonzhou.learn.jvm.klass;

public class Foo {
private int m;

public int inc() {
return m + 1;
}
}

Class结构体

一个Class文件可以用如下的结构体抽象:

1556850581334

Step by Step

接下来就是对照着字节码和JVM规范阅读的过程,对自己多点耐心。

Class魔数和版本

1556854654580

1556850710537

img

img

常量池计数器和常量池

1556854680622

注意:常量池内常量的真实数量是 常量池计数器-1

1556850772017

1556850796593

1556850817023

1556850841470

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

img

那么接下来就是这三个了:

1556854982133

访问标志

1556855229233

1556851149393

这里

img

为什么是0021呢?因为 0X0001|0X0020 = 0X0021。

类索引,父类索引

1556855257629

接下来的2B是this_class指向我们的类名, super_class指示父类。

img

img

这里没有实现接口:

img

字段计数器和字段表集合

fields_count以后进入字段表集合

1556851584435

1556851428143

1556851512040

img

方法计数器和方发表集合

首先进入方法计数器。

1556852236787

img

1556852171723

1556851428143

从上可以看出,关键的不同点在于attribute_info字段。这是属性表集合,下面附上属性表集合的一般结构。

1556852397696

1556852498092

从上面两个图可以看出属性表前两个字段时固定的,关键在于第三个字段各有不同,比如方法表的属性表Code的完整格式为下图

1556853118948

接下来就对Code属性表进行梳理:

img

img

属性计数器和属性表集合

1556856126125

属性表集合中的SourceFile:1556854138367

1556854215807

由属性计数器后的0010计算十进制数为

1556855978446

下面的DEC为16,所以找到第16个常量为1556856030704

没错了,是SourceFile

img

附上javap -version 验证解析结果

使用javap解析出来的结果中没有包含LocalVariableTable字段,需要在使用javac编译java中时,加上-g的参数,生成的class文件中才带有LocalVariableTable的信息。LocalVariableTable属性:用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,非运行时必需属性,默认不会生成至Class文件中,可以使用Javac的-g:none或-g:vars关闭或要求生成该项属性信息。

1556860378700

另外:LineNumberTale属性:用于描述Java源码的行号与字节码行号之间的对应关系,非运行时必需属性,会默认生成至Class文件中,可以使用Javac的-g:none或-g:lines关闭或要求生成该项属性信息。

1556860512094

具体编译过程:

1
javac -g -d . Foo.java
1
javap -v com.vonzhou.learn.jvm.klass.Foo
1
ca fe ba be 20 20 20 34 20 16 0a 20 04 20 12 09 20 03 20 13 07 20 14 07 20 15 01 20 01 6d 01 20 01 49 01 20 06 3c 69 6e 69 74 3e 01 20 03 28 29 56 01 20 04 43 6f 64 65 01 20 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 20 12 4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01 20 04 74 68 69 73 01 20 21 4c 63 6f 6d 2f 76 6f 6e 7a 68 6f 75 2f 6c 65 61 72 6e 2f 6a 76 6d 2f 6b 6c 61 73 73 2f 46 6f 6f 3b 01 20 03 69 6e 63 01 20 03 28 29 49 01 20 0a 53 6f 75 72 63 65 46 69 6c 65 01 20 08 46 6f 6f 2e 6a 61 76 61 0c 20 07 20 08 0c 20 05 20 06 01 20 1f 63 6f 6d 2f 76 6f 6e 7a 68 6f 75 2f 6c 65 61 72 6e 2f 6a 76 6d 2f 6b 6c 61 73 73 2f 46 6f 6f 01 20 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 20 21 20 03 20 04 20 20 20 01 20 02 20 05 20 06 20 20 20 02 20 01 20 07 20 08 20 01 20 09 20 20 20 2f 20 01 20 01 20 20 20 05 2a b7 20 01 b1 20 20 20 02 20 0a 20 20 20 06 20 01 20 20 20 03 20 0b 20 20 20 0c 20 01 20 20 20 05 20 0c 20 0d 20 20 20 01 20 0e 20 0f 20 01 20 09 20 20 20 31 20 02 20 01 20 20 20 07 2a b4 20 02 04 60 ac 20 20 20 02 20 0a 20 20 20 06 20 01 20 20 20 07 20 0b 20 20 20 0c 20 01 20 20 20 07 20 0c 20 0d 20 20 20 01 20 10 20 20 20 02 20 11
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
54
55
56
57
58
59
60
61
62
63
E:\JVM>javap -v com.vonzhou.learn.jvm.klass.Foo
Classfile /E:/JVM/com/vonzhou/learn/jvm/klass/Foo.class
Last modified 2019-5-3; size 391 bytes
MD5 checksum 4d9e593620f49a9114d834ec5d923986
Compiled from "Foo.java"
public class com.vonzhou.learn.jvm.klass.Foo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // com/vonzhou/learn/jvm/klass/Foo.m:I
#3 = Class #20 // com/vonzhou/learn/jvm/klass/Foo
#4 = Class #21 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/vonzhou/learn/jvm/klass/Foo;
#14 = Utf8 inc
#15 = Utf8 ()I
#16 = Utf8 SourceFile
#17 = Utf8 Foo.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = NameAndType #5:#6 // m:I
#20 = Utf8 com/vonzhou/learn/jvm/klass/Foo
#21 = Utf8 java/lang/Object
{
public com.vonzhou.learn.jvm.klass.Foo();
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 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/vonzhou/learn/jvm/klass/Foo;

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 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/vonzhou/learn/jvm/klass/Foo;
}
SourceFile: "Foo.java"

再次附上没有 -g 的结果:

1
ca fe ba be 20 20 20 34 20 13 0a 20 04 20 0f 09 20 03 20 10 07 20 11 07 20 12 01 20 01 6d 01 20 01 49 01 20 06 3c 69 6e 69 74 3e 01 20 03 28 29 56 01 20 04 43 6f 64 65 01 20 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 20 03 69 6e 63 01 20 03 28 29 49 01 20 0a 53 6f 75 72 63 65 46 69 6c 65 01 20 08 46 6f 6f 2e 6a 61 76 61 0c 20 07 20 08 0c 20 05 20 06 01 20 1f 63 6f 6d 2f 76 6f 6e 7a 68 6f 75 2f 6c 65 61 72 6e 2f 6a 76 6d 2f 6b 6c 61 73 73 2f 46 6f 6f 01 20 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 20 21 20 03 20 04 20 20 20 01 20 02 20 05 20 06 20 20 20 02 20 01 20 07 20 08 20 01 20 09 20 20 20 1d 20 01 20 01 20 20 20 05 2a b7 20 01 b1 20 20 20 01 20 0a 20 20 20 06 20 01 20 20 20 03 20 01 20 0b 20 0c 20 01 20 09 20 20 20 1f 20 02 20 01 20 20 20 07 2a b4 20 02 04 60 ac 20 20 20 01 20 0a 20 20 20 06 20 01 20 20 20 07 20 01 20 0d 20 20 20 02 20 0e
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
54
55
56
E:\JVM>javac  -d . Foo.java

E:\JVM>javap -v com.vonzhou.learn.jvm.klass.Foo
Classfile /E:/JVM/com/vonzhou/learn/jvm/klass/Foo.class
Last modified 2019-5-3; size 291 bytes
MD5 checksum 45262c23d72e75c78347d2f05b918bee
Compiled from "Foo.java"
public class com.vonzhou.learn.jvm.klass.Foo
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 // com/vonzhou/learn/jvm/klass/Foo.m:I
#3 = Class #17 // com/vonzhou/learn/jvm/klass/Foo
#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 Foo.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = NameAndType #5:#6 // m:I
#17 = Utf8 com/vonzhou/learn/jvm/klass/Foo
#18 = Utf8 java/lang/Object
{
public com.vonzhou.learn.jvm.klass.Foo();
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 3: 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 7: 0
}
SourceFile: "Foo.java"

参考

《深入理解Java虚拟机》周志明,

另外找到的其他关于Class文件解析的文章有:

https://www.cnblogs.com/timlong/p/8143839.html

https://www.cnblogs.com/noteless/p/9540876.html#0

https://www.jianshu.com/p/d0f3e361f92e

https://www.jb51.net/article/116203.htm(这个有对文中没有详细解释的method方法的属性的解释)

-------------本文结束感谢您的阅读-------------
愿你所有幸运,都不期而遇;愿你所有美好,都如约而至。