jvm-字节码文件

下面解析一段源代码

  • jdk版本 1.8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TestClass {
private int m;
private Integer i;
public int inc(){
// long a = 0;
//
// long b = 0;
//
// Integer c = 0;
// i++; //拆箱intvalue 装箱valueof
return m+1;
}

public TestClass(){
// i = 2; //装箱
// i.toString();
}

// public static void main(String[] args){
// System.out.println(new FieldResolution().getClass().getClassLoader());
// System.out.println(new String("aaaaa").getClass().getClassLoader());
// System.out.println(new Date().getClass().getClassLoader());
// }
}

编译后的字节码文件

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
Classfile /media/zhangbo/resource/javaProgram/JVM/src/cn/swpu/test/TestClass.class
Last modified 2019-3-23; size 326 bytes
MD5 checksum 6933bec5f3baf9f936b45a4704ca4d44
Compiled from "TestClass.java"
public class cn.swpu.test.TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Fieldref #3.#17 // cn/swpu/test/TestClass.m:I
#2 = Methodref #4.#18 // java/lang/Object."<init>":()V
#3 = Class #19 // cn/swpu/test/TestClass
#4 = Class #20 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 i
#8 = Utf8 Ljava/lang/Integer;
#9 = Utf8 inc
#10 = Utf8 ()I
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 SourceFile
#16 = Utf8 TestClass.java
#17 = NameAndType #5:#6 // m:I
#18 = NameAndType #13:#14 // "<init>":()V
#19 = Utf8 cn/swpu/test/TestClass
#20 = Utf8 java/lang/Object
{
public int inc();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field m:I
4: iconst_1
5: iadd
6: ireturn
LineNumberTable:
line 14: 0

public cn.swpu.test.TestClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 18: 0
line 21: 4
}

字节码文件前面是一些校验和信息,jdk版本信息,class文件的访问权限信息

理解–constant pool

#1 = Fieldref #3.#17 // cn/swpu/test/TestClass.m:I

第一个常量 它表示对一个类变量的引用,一号常量又引用了#3号常量和#17号常量,分别表示它的所属类,m是它的名称,I是它的类型描述符,表示int。

这里发现源代码中有Integer类型的变量i,但是字节码文件中并没有生成对他的Contant_field_ref常量,原因在代码中并没又使用i变量,所以被编译器优化掉了。

#2 = Methodref #4.#18 // java/lang/Object.”“:()V

编译器在字节码文件中添加了一个返回值为void,名称为的方法,这个方法是属于Object类的。

这个类是源码中没有的,编译器为我们添加的实例构造器。

这时我们的实例构造器是空的,为它添加上一句代码

1
2
3
public TestClass(){
i = 2;
}

invokespecial and invokestatic

​ 这是编译后的无参数构造器的字节码,出现了两个很特的指令invokespecial 和 invokestatic, - 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法。(Invoke instance method; special handling for superclass, private, and instance initialization method invocations )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public cn.swpu.test.TestClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_2
6: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: putfield #4 // Field i:Ljava/lang/Integer;
12: return
LineNumberTable:
line 18: 0
line 19: 4
line 22: 12
}

可以看到编译器把调用父类构造器的代码,和TestClass本身构造器的代码合并了。那么多个重载构造器会出现什么情况呢

1
2
3
4
5
6
7
8
public TestClass(){
i = 2;
}

public TestClass(Integer i) {
this.i = i;
i++;
}

编译后……编译器在方法表里生成了一个重载的方法,描述符 (Ljava/lang/Integer;)V,同样也添加了调用Object构造器的代码

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
public cn.swpu.test.TestClass(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=2
0: aload_0
1: invokespecial #2 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #4 // Field i:Ljava/lang/Integer;
9: aload_1
10: astore_2
11: aload_1
12: invokevirtual #5 // Method java/lang/Integer.intValue:()I
15: iconst_1
16: iadd
17: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
20: dup
21: astore_1
22: astore_3
23: aload_2
24: pop
25: return
LineNumberTable:
line 24: 0
line 25: 4
line 26: 9
line 27: 25
}

从 3 句 incokespecial 语句可以看到,实际上构造器的代码都被放进了实例构造器中。

#3 = Class #19 // cn/swpu/test/TestClass 引用了19号utf8编码的字符串 “cn/swpu/test/TestClass”
#4 = Class #20 // java/lang/Object 引用了20号utf8编码的字符串“ java/lang/Object”