4. 程序计数器
4.1 PC Register介绍
2.5.1. The pc Register
The Java Virtual Machine can support many threads of execution at once (JLS §17). Each Java Virtual Machine thread has its own pc (program counter) register. At any point, each Java Virtual Machine thread is executing the code of a single method, namely the current method (§2.6) for that thread. If that method is not native, the pc register contains the address of the Java Virtual Machine instruction currently being executed. If the method currently being executed by the thread is native, the value of the Java Virtual Machine's pc register is undefined. The Java Virtual Machine's pc register is wide enough to hold a returnAddress or a native pointer on the specific platform.
JVM 中的程序计数寄存器(Program Counter Register)中,Register 的命名源于 CPU 的寄存器,寄存器存储指令相关的现场信息。CPU 只有把数据装载到寄存器才能够运行。
这里,并非是广义上所指的物理寄存器,或许将其翻译为 PC 计数器(或指令计数器)会更加贴切(也称为程序钩子),并且不容易引起一些不必要的误会。JVM 中的 PC 寄存器是对物理 PC 寄存器的一种抽象模拟。
4.1.1 作用
PC 寄存器是用来存储指向下一条指令的地址,也即将将要执行的指令代码。由执行引擎读取下一条指令。
- 它是一块很小的内存空间,几乎可以忽略不计。也是运行速度最快的存储区域
- 在 JVM 规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致
- 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的 Java 方法的 JVM 指令地址;或者,如果实在执行 native 方法,则是未指定值(undefined)。
- 它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成
- 字节码解释器工作时就是通过改变这个计数器的值来选取吓一跳需要执行的字节码指令
- 它是唯一一个在 Java 虚拟机规范中没有规定任何 OOM(OutOfMemoryError)情况的区域
- 没有垃圾回收(GC),没有 OOM
4.2 举例说明
- PcRegisterTest
- main 方法字节码
- 完整字节码
public class PcRegisterTest {
public static void main(String[] args) {
int i = 10;
int j = 20;
int k = i + j;
}
}
0 bipush 10
2 istore_1
3 bipush 20
5 istore_2
6 iload_1
7 iload_2
8 iadd
9 istore_3
10 return
$ javap -verbose PCRegisterTest.class
Classfile /Users/wangchengwei/jvm-study/target/classes/org/depsea/jvm/chapter04/PCRegisterTest.class
Last modified 2021年5月16日; size 503 bytes
SHA-256 checksum 0e9adeb1093b70015f35559ec62dbe28d31a2e13f511ea999ea666f07c444ac8
Compiled from "PCRegisterTest.java"
public class org.depsea.jvm.chapter04.PCRegisterTest
minor version: 0
major version: 52
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #2 // org/depsea/jvm/chapter04/PCRegisterTest
super_class: #3 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #3.#21 // java/lang/Object."<init>":()V
#2 = Class #22 // org/depsea/jvm/chapter04/PCRegisterTest
#3 = Class #23 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lorg/depsea/jvm/chapter04/PCRegisterTest;
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 args
#14 = Utf8 [Ljava/lang/String;
#15 = Utf8 i
#16 = Utf8 I
#17 = Utf8 j
#18 = Utf8 k
#19 = Utf8 SourceFile
#20 = Utf8 PCRegisterTest.java
#21 = NameAndType #4:#5 // "<init>":()V
#22 = Utf8 org/depsea/jvm/chapter04/PCRegisterTest
#23 = Utf8 java/lang/Object
{
public org.depsea.jvm.chapter04.PCRegisterTest();
descriptor: ()V
flags: (0x0001) 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 22: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lorg/depsea/jvm/chapter04/PCRegisterTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 10
2: istore_1
3: bipush 20
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: return
LineNumberTable:
line 25: 0
line 26: 3
line 27: 6
line 28: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 args [Ljava/lang/String;
3 8 1 i I
6 5 2 j I
10 1 3 k I
}
SourceFile: "PCRegisterTest.java"
再引入一些其他的结构。
- PcRegisterTest
- main 方法字节码
- 完整字节码
public class PcRegisterTest {
public static void main(String[] args) {
int i = 10;
int j = 20;
int k = i + j;
String s = "abc";
System.out.println(i);
System.out.println(k);
}
}
0: bipush 10
2: istore_1
3: bipush 20
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: ldc #2 // String abc
12: astore 4
14: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_1
18: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
21: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_3
25: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
28: return
$ javap -v chapter04/PCRegisterTest.class
Classfile /Users/wangchengwei/jvm-study/target/classes/org/depsea/jvm/chapter04/PCRegisterTest.class
Last modified 2021年5月16日; size 691 bytes
SHA-256 checksum ce3ce0be4d17a9c283af01acb27585080e94d87f86981974178c640ad95c90a2
Compiled from "PCRegisterTest.java"
public class org.depsea.jvm.chapter04.PCRegisterTest
minor version: 0
major version: 52
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #5 // org/depsea/jvm/chapter04/PCRegisterTest
super_class: #6 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Methodref #6.#26 // java/lang/Object."<init>":()V
#2 = String #27 // abc
#3 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
#5 = Class #32 // org/depsea/jvm/chapter04/PCRegisterTest
#6 = Class #33 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lorg/depsea/jvm/chapter04/PCRegisterTest;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 i
#19 = Utf8 I
#20 = Utf8 j
#21 = Utf8 k
#22 = Utf8 s
#23 = Utf8 Ljava/lang/String;
#24 = Utf8 SourceFile
#25 = Utf8 PCRegisterTest.java
#26 = NameAndType #7:#8 // "<init>":()V
#27 = Utf8 abc
#28 = Class #34 // java/lang/System
#29 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
#30 = Class #37 // java/io/PrintStream
#31 = NameAndType #38:#39 // println:(I)V
#32 = Utf8 org/depsea/jvm/chapter04/PCRegisterTest
#33 = Utf8 java/lang/Object
#34 = Utf8 java/lang/System
#35 = Utf8 out
#36 = Utf8 Ljava/io/PrintStream;
#37 = Utf8 java/io/PrintStream
#38 = Utf8 println
#39 = Utf8 (I)V
{
public org.depsea.jvm.chapter04.PCRegisterTest();
descriptor: ()V
flags: (0x0001) 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 22: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lorg/depsea/jvm/chapter04/PCRegisterTest;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: bipush 10
2: istore_1
3: bipush 20
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: ldc #2 // String abc
12: astore 4
14: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_1
18: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
21: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
24: iload_3
25: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
28: return
LineNumberTable:
line 25: 0
line 26: 3
line 27: 6
line 29: 10
line 30: 14
line 31: 21
line 32: 28
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
3 26 1 i I
6 23 2 j I
10 19 3 k I
14 15 4 s Ljava/lang/String;
}
SourceFile: "PCRegisterTest.java"
字节码中的 #n
对应的是常量池(Constant pool)中的内容。如上面序号为 10 的字节码 10: ldc #2
对应的就是常量池中的 #2 = String #27
,#27
又对应常量池中的 #27 = Utf8 abc