第一章 JVM 概述
第二章 JVM 编译
第三章 JVM 类结构
第四章 JVM 类加载机制
本章描述的编译是指使用 JDK 自带的 java 工具把源代码编译成 class 文件,重点关注如何把 java 语言编译成 jvm 指令。
用户可以使用 javap 工具把二进制的 class 文件转换成用户可以看懂的 jvm 指令文本,来方便我们理解编译后的 class 文件,-p 参数会展示私有的方法的属性,-v 参数会展示尽可能多的信息。示例:javap -v -p HelloWorld。如果你使用了编译器 idea,可以使用 jclasslib 插件来查看 class 文件信息。
指令的格式:
index 是指从方法开始的位置到当前指令的偏移量
opcode 是指操作码
operand 是指操作数,操作码的参数
comment 由编译器或者用户生成的注释
以下代码对常用的 Java 语法做了示例,可以通过方法名去找对应的字节码实现。字节码实现基本可以和方法一一对应。
// 新建类实例、操作数栈相关指令
JavaCompiler compiler = new JavaCompiler();
0: new #2 // class org/compiler/JavaCompiler
3: dup
4: invokespecial #3 // Method "":()V
7: astore_1
8: aload_1
参照指令格式 0: new #2 // class org/compiler/JavaCompiler
,0 代表当前指令在方法中的偏移量是0,代表第一条指令,new
是操作符也就是是指令名称,表示当前指令用来创建对象实例,#2
是指令的操作数即参数,# 代表内容需要从字节码的常量池 Constant poll
中获取,常量池中 #2 对应的内容为 #76 对应的内容 org/compiler/JavaCompiler
代表类的全限定名,也就是要创建对象的类型。最后一列的 comment 也帮我们标明了实际的值。执行完这条指令,JVM 就会创建一个 JavaCompiler
实例,并把它放入操作数栈中。
Constant pool:
#1 = Methodref #24.#75 // java/lang/Object."":()V
#2 = Class #76 // org/compiler/JavaCompiler
#76 = Utf8 org/compiler/JavaCompiler
执行完第二条指令 dup
,在操作数栈中拷贝栈顶元素并推入栈内,此时本内有两个 this。
执行第三条指令 invokespecial #3
会调用对象的 init
方法并做初始化,需要从栈内弹出一个 this 参数,但是此方法无返回值,此时栈内还剩一个 this。
执行第四条指令 astore_1
把弹出一个操作数栈元素,并保存在本地变量 1 中。
执行第五条指令 aload_1
又把本地变量 1 的值推入操作数栈中供后面的方法使用。
基础的指令知识请参考Compiling for the Java Virtual Machine
dup
指令,dup 是操作数栈的指令,它不需要关心数据的类型,作用是拷贝一份和当前栈顶元素一模一样的数据并推入栈内,作用是在对象初始化的时候为初始化后的 aload_n 提供 this 操作数,栈中 this 被 invokeSpecial init() 拿去执行,初始化方法没有返回值,如果不 dup 一个栈中就没有 this 了。public class JavaCompiler {
private static final Integer LIMIT_100 = 100;
private static final String FINALLY = "finally";
public static void main(String[] args) {
try {
// 访问运行时常量池
// 新建类实例、操作数栈相关指令
JavaCompiler compiler = new JavaCompiler();
// 调用方法、接收参数
compiler.setCount(0);
// 同步代码块
int size = compiler.synchronizedBlockAddTwoInteger(compiler.addToLimit(), LIMIT_100);
// 数组
int[] array = new int[size];
final int mod = size / LIMIT_100;
int switchResult;
// switch
switch (mod) {
case 0: switchResult = 0; break;
case 1: switchResult = 1; break;
case 2: switchResult = 2; break;
case 5: switchResult = 5; break;
case 10: switchResult = 10; break;
case 20: switchResult = 20; break;
case 30: switchResult = 30; break;
default: switchResult = 99;
}
// 调用静态方法, 接收参数,同步方法
System.out.println("result:" + compiler.synchronizedAddTwoInteger(JavaCompiler.staticAddTwoInteger(switchResult,mod),LIMIT_100));
} catch (Exception e) {
// 异常处理指令
System.out.println(e.getMessage());
} finally {
// finally 指令
System.out.println(FINALLY);
}
}
int addToLimit () {
// 常量 0, 100 控制结构
for (int i = 0; i < LIMIT_100; i++) {
// 计算指令
count += i;
}
return count;
}
/**
* 调用实例方法, 接收参数
*/
int synchronizedBlockAddTwoInteger (Integer intA, Integer intB) {
synchronized (this) {
return intA + intB;
}
}
/**
* 同步调用实例方法, 接收参数
*/
synchronized int synchronizedAddTwoInteger (Integer intA, Integer intB) {
return intA + intB;
}
/**
* 调用静态方法, 接收参数
*/
public static int staticAddTwoInteger (Integer intA, Integer intB) {
return intA + intB;
}
/**
* 本地变量
*/
private Integer count;
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
}
javap -v -p JavaCompiler
$ javap -v -p JavaCompiler
Warning: Binary file JavaCompiler contains org.compiler.JavaCompiler
Classfile /Users/geguirong/Downloads/project/mchz/no_maven/out/production/no_maven/org/compiler/JavaCompiler.class
Last modified Mar 23, 2022; size 2667 bytes
MD5 checksum 1f9d9cc17562aaf4093ff03008077c51
Compiled from "JavaCompiler.java"
public class org.compiler.JavaCompiler
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #24.#75 // java/lang/Object."":()V
#2 = Class #76 // org/compiler/JavaCompiler
#3 = Methodref #2.#75 // org/compiler/JavaCompiler."":()V
#4 = Methodref #77.#78 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#5 = Methodref #2.#79 // org/compiler/JavaCompiler.setCount:(Ljava/lang/Integer;)V
#6 = Methodref #2.#80 // org/compiler/JavaCompiler.addToLimit:()I
#7 = Fieldref #2.#81 // org/compiler/JavaCompiler.LIMIT_100:Ljava/lang/Integer;
#8 = Methodref #2.#82 // org/compiler/JavaCompiler.synchronizedBlockAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
#9 = Methodref #77.#83 // java/lang/Integer.intValue:()I
#10 = Fieldref #84.#85 // java/lang/System.out:Ljava/io/PrintStream;
#11 = Class #86 // java/lang/StringBuilder
#12 = Methodref #11.#75 // java/lang/StringBuilder."":()V
#13 = String #87 // result:
#14 = Methodref #11.#88 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#15 = Methodref #2.#89 // org/compiler/JavaCompiler.staticAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
#16 = Methodref #2.#90 // org/compiler/JavaCompiler.synchronizedAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
#17 = Methodref #11.#91 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#18 = Methodref #11.#92 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#19 = Methodref #93.#94 // java/io/PrintStream.println:(Ljava/lang/String;)V
#20 = String #95 // finally
#21 = Class #96 // java/lang/Exception
#22 = Methodref #21.#97 // java/lang/Exception.getMessage:()Ljava/lang/String;
#23 = Fieldref #2.#98 // org/compiler/JavaCompiler.count:Ljava/lang/Integer;
#24 = Class #99 // java/lang/Object
#25 = Utf8 LIMIT_100
#26 = Utf8 Ljava/lang/Integer;
#27 = Utf8 FINALLY
#28 = Utf8 Ljava/lang/String;
#29 = Utf8 ConstantValue
#30 = Utf8 count
#31 = Utf8
#32 = Utf8 ()V
#33 = Utf8 Code
#34 = Utf8 LineNumberTable
#35 = Utf8 LocalVariableTable
#36 = Utf8 this
#37 = Utf8 Lorg/compiler/JavaCompiler;
#38 = Utf8 main
#39 = Utf8 ([Ljava/lang/String;)V
#40 = Utf8 switchResult
#41 = Utf8 I
#42 = Utf8 compiler
#43 = Utf8 size
#44 = Utf8 array
#45 = Utf8 [I
#46 = Utf8 mod
#47 = Utf8 e
#48 = Utf8 Ljava/lang/Exception;
#49 = Utf8 args
#50 = Utf8 [Ljava/lang/String;
#51 = Utf8 StackMapTable
#52 = Class #50 // "[Ljava/lang/String;"
#53 = Class #76 // org/compiler/JavaCompiler
#54 = Class #45 // "[I"
#55 = Class #96 // java/lang/Exception
#56 = Class #100 // java/lang/Throwable
#57 = Utf8 addToLimit
#58 = Utf8 ()I
#59 = Utf8 i
#60 = Utf8 synchronizedBlockAddTwoInteger
#61 = Utf8 (Ljava/lang/Integer;Ljava/lang/Integer;)I
#62 = Utf8 intA
#63 = Utf8 intB
#64 = Class #101 // java/lang/Integer
#65 = Class #99 // java/lang/Object
#66 = Utf8 synchronizedAddTwoInteger
#67 = Utf8 staticAddTwoInteger
#68 = Utf8 getCount
#69 = Utf8 ()Ljava/lang/Integer;
#70 = Utf8 setCount
#71 = Utf8 (Ljava/lang/Integer;)V
#72 = Utf8
#73 = Utf8 SourceFile
#74 = Utf8 JavaCompiler.java
#75 = NameAndType #31:#32 // "":()V
#76 = Utf8 org/compiler/JavaCompiler
#77 = Class #101 // java/lang/Integer
#78 = NameAndType #102:#103 // valueOf:(I)Ljava/lang/Integer;
#79 = NameAndType #70:#71 // setCount:(Ljava/lang/Integer;)V
#80 = NameAndType #57:#58 // addToLimit:()I
#81 = NameAndType #25:#26 // LIMIT_100:Ljava/lang/Integer;
#82 = NameAndType #60:#61 // synchronizedBlockAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
#83 = NameAndType #104:#58 // intValue:()I
#84 = Class #105 // java/lang/System
#85 = NameAndType #106:#107 // out:Ljava/io/PrintStream;
#86 = Utf8 java/lang/StringBuilder
#87 = Utf8 result:
#88 = NameAndType #108:#109 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#89 = NameAndType #67:#61 // staticAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
#90 = NameAndType #66:#61 // synchronizedAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
#91 = NameAndType #108:#110 // append:(I)Ljava/lang/StringBuilder;
#92 = NameAndType #111:#112 // toString:()Ljava/lang/String;
#93 = Class #113 // java/io/PrintStream
#94 = NameAndType #114:#115 // println:(Ljava/lang/String;)V
#95 = Utf8 finally
#96 = Utf8 java/lang/Exception
#97 = NameAndType #116:#112 // getMessage:()Ljava/lang/String;
#98 = NameAndType #30:#26 // count:Ljava/lang/Integer;
#99 = Utf8 java/lang/Object
#100 = Utf8 java/lang/Throwable
#101 = Utf8 java/lang/Integer
#102 = Utf8 valueOf
#103 = Utf8 (I)Ljava/lang/Integer;
#104 = Utf8 intValue
#105 = Utf8 java/lang/System
#106 = Utf8 out
#107 = Utf8 Ljava/io/PrintStream;
#108 = Utf8 append
#109 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#110 = Utf8 (I)Ljava/lang/StringBuilder;
#111 = Utf8 toString
#112 = Utf8 ()Ljava/lang/String;
#113 = Utf8 java/io/PrintStream
#114 = Utf8 println
#115 = Utf8 (Ljava/lang/String;)V
#116 = Utf8 getMessage
{
private static final java.lang.Integer LIMIT_100;
descriptor: Ljava/lang/Integer;
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
private static final java.lang.String FINALLY;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
ConstantValue: String finally
private java.lang.Integer count;
descriptor: Ljava/lang/Integer;
flags: ACC_PRIVATE
public org.compiler.JavaCompiler();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lorg/compiler/JavaCompiler;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=7, args_size=1
0: new #2 // class org/compiler/JavaCompiler
3: dup
4: invokespecial #3 // Method "":()V
7: astore_1
8: aload_1
9: iconst_0
10: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokevirtual #5 // Method setCount:(Ljava/lang/Integer;)V
16: aload_1
17: aload_1
18: invokevirtual #6 // Method addToLimit:()I
21: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
24: getstatic #7 // Field LIMIT_100:Ljava/lang/Integer;
27: invokevirtual #8 // Method synchronizedBlockAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
30: istore_2
31: iload_2
32: newarray int
34: astore_3
35: iload_2
36: getstatic #7 // Field LIMIT_100:Ljava/lang/Integer;
39: invokevirtual #9 // Method java/lang/Integer.intValue:()I
42: idiv
43: istore 4
45: iload 4
47: lookupswitch { // 7
0: 112
1: 118
2: 124
5: 130
10: 136
20: 143
30: 150
default: 157
}
112: iconst_0
113: istore 5
115: goto 161
118: iconst_1
119: istore 5
121: goto 161
124: iconst_2
125: istore 5
127: goto 161
130: iconst_5
131: istore 5
133: goto 161
136: bipush 10
138: istore 5
140: goto 161
143: bipush 20
145: istore 5
147: goto 161
150: bipush 30
152: istore 5
154: goto 161
157: bipush 99
159: istore 5
161: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
164: new #11 // class java/lang/StringBuilder
167: dup
168: invokespecial #12 // Method java/lang/StringBuilder."":()V
171: ldc #13 // String result:
173: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
176: aload_1
177: iload 5
179: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
182: iload 4
184: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
187: invokestatic #15 // Method staticAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
190: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
193: getstatic #7 // Field LIMIT_100:Ljava/lang/Integer;
196: invokevirtual #16 // Method synchronizedAddTwoInteger:(Ljava/lang/Integer;Ljava/lang/Integer;)I
199: invokevirtual #17 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
202: invokevirtual #18 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
205: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
208: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
211: ldc #20 // String finally
213: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
216: goto 254
219: astore_1
220: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
223: aload_1
224: invokevirtual #22 // Method java/lang/Exception.getMessage:()Ljava/lang/String;
227: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
230: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
233: ldc #20 // String finally
235: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
238: goto 254
241: astore 6
243: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
246: ldc #20 // String finally
248: invokevirtual #19 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
251: aload 6
253: athrow
254: return
Exception table:
from to target type
0 208 219 Class java/lang/Exception
0 208 241 any
219 230 241 any
241 243 241 any
LineNumberTable:
line 16: 0
line 18: 8
line 20: 16
line 22: 31
line 23: 35
line 26: 45
line 27: 112
line 28: 118
line 29: 124
line 30: 130
line 31: 136
line 32: 143
line 33: 150
line 34: 157
line 37: 161
line 43: 208
line 44: 216
line 38: 219
line 40: 220
line 43: 230
line 44: 238
line 43: 241
line 44: 251
line 45: 254
LocalVariableTable:
Start Length Slot Name Signature
115 3 5 switchResult I
121 3 5 switchResult I
127 3 5 switchResult I
133 3 5 switchResult I
140 3 5 switchResult I
147 3 5 switchResult I
154 3 5 switchResult I
8 200 1 compiler Lorg/compiler/JavaCompiler;
31 177 2 size I
35 173 3 array [I
45 163 4 mod I
161 47 5 switchResult I
220 10 1 e Ljava/lang/Exception;
0 255 0 args [Ljava/lang/String;
StackMapTable: number_of_entries = 12
frame_type = 255 /* full_frame */
offset_delta = 112
locals = [ class "[Ljava/lang/String;", class org/compiler/JavaCompiler, int, class "[I", int ]
stack = []
frame_type = 5 /* same */
frame_type = 5 /* same */
frame_type = 5 /* same */
frame_type = 5 /* same */
frame_type = 6 /* same */
frame_type = 6 /* same */
frame_type = 6 /* same */
frame_type = 252 /* append */
offset_delta = 3
locals = [ int ]
frame_type = 255 /* full_frame */
offset_delta = 57
locals = [ class "[Ljava/lang/String;" ]
stack = [ class java/lang/Exception ]
frame_type = 85 /* same_locals_1_stack_item */
stack = [ class java/lang/Throwable ]
frame_type = 12 /* same */
int addToLimit();
descriptor: ()I
flags:
Code:
stack=3, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: getstatic #7 // Field LIMIT_100:Ljava/lang/Integer;
6: invokevirtual #9 // Method java/lang/Integer.intValue:()I
9: if_icmpge 34
12: aload_0
13: aload_0
14: getfield #23 // Field count:Ljava/lang/Integer;
17: invokevirtual #9 // Method java/lang/Integer.intValue:()I
20: iload_1
21: iadd
22: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
25: putfield #23 // Field count:Ljava/lang/Integer;
28: iinc 1, 1
31: goto 2
34: aload_0
35: getfield #23 // Field count:Ljava/lang/Integer;
38: invokevirtual #9 // Method java/lang/Integer.intValue:()I
41: ireturn
LineNumberTable:
line 50: 0
line 52: 12
line 50: 28
line 54: 34
LocalVariableTable:
Start Length Slot Name Signature
2 32 1 i I
0 42 0 this Lorg/compiler/JavaCompiler;
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 250 /* chop */
offset_delta = 31
int synchronizedBlockAddTwoInteger(java.lang.Integer, java.lang.Integer);
descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)I
flags:
Code:
stack=2, locals=5, args_size=3
0: aload_0
1: dup
2: astore_3
3: monitorenter
4: aload_1
5: invokevirtual #9 // Method java/lang/Integer.intValue:()I
8: aload_2
9: invokevirtual #9 // Method java/lang/Integer.intValue:()I
12: iadd
13: aload_3
14: monitorexit
15: ireturn
16: astore 4
18: aload_3
19: monitorexit
20: aload 4
22: athrow
Exception table:
from to target type
4 15 16 any
16 20 16 any
LineNumberTable:
line 61: 0
line 62: 4
line 63: 16
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 this Lorg/compiler/JavaCompiler;
0 23 1 intA Ljava/lang/Integer;
0 23 2 intB Ljava/lang/Integer;
StackMapTable: number_of_entries = 1
frame_type = 255 /* full_frame */
offset_delta = 16
locals = [ class org/compiler/JavaCompiler, class java/lang/Integer, class java/lang/Integer, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
synchronized int synchronizedAddTwoInteger(java.lang.Integer, java.lang.Integer);
descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)I
flags: ACC_SYNCHRONIZED
Code:
stack=2, locals=3, args_size=3
0: aload_1
1: invokevirtual #9 // Method java/lang/Integer.intValue:()I
4: aload_2
5: invokevirtual #9 // Method java/lang/Integer.intValue:()I
8: iadd
9: ireturn
LineNumberTable:
line 70: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lorg/compiler/JavaCompiler;
0 10 1 intA Ljava/lang/Integer;
0 10 2 intB Ljava/lang/Integer;
public static int staticAddTwoInteger(java.lang.Integer, java.lang.Integer);
descriptor: (Ljava/lang/Integer;Ljava/lang/Integer;)I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokevirtual #9 // Method java/lang/Integer.intValue:()I
4: aload_1
5: invokevirtual #9 // Method java/lang/Integer.intValue:()I
8: iadd
9: ireturn
LineNumberTable:
line 77: 0
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 intA Ljava/lang/Integer;
0 10 1 intB Ljava/lang/Integer;
public java.lang.Integer getCount();
descriptor: ()Ljava/lang/Integer;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #23 // Field count:Ljava/lang/Integer;
4: areturn
LineNumberTable:
line 86: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lorg/compiler/JavaCompiler;
public void setCount(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #23 // Field count:Ljava/lang/Integer;
5: return
LineNumberTable:
line 90: 0
line 91: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lorg/compiler/JavaCompiler;
0 6 1 count Ljava/lang/Integer;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 100
2: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: putstatic #7 // Field LIMIT_100:Ljava/lang/Integer;
8: return
LineNumberTable:
line 9: 0
}
SourceFile: "JavaCompiler.java"