程序计数器(Program Counter Register)
基本概述
JVM的程序计数寄存器(Program Counter Register)中,Register的命名来源于CPU寄存器,寄存器的作用是存储指令相关的现场信息,CPU只有把数据装载到寄存器才能够运行。JVM中PC寄存器是对物理PC寄存器的一种抽象模型。
特性
- 程序计数器是一块小到几乎可以忽略不计的内存空间,也是JVM运行速度最快的存储区域
- 程序计数器可以看作是当前线程所执行的字节码的行号指示器。在java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器。分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器。
- 由于java虚拟机的多线程是通过处理器时间片轮流切换来实现的,为了线程切换后能恢复到正确的执行位置,每条线程都应有它自己的程序计数器,且是线程私有的,各条线程之间计数器互不影响,独立存储,它的生命周期和线程的生命周期一致。
- 任何时间,一个线程都只能有一个方法在执行,这个方法我们称为当前方法。如果当前方法是一个Java方法,那么这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的当前方法是本地(Native)方法,则这个计数器值则应为空(Undefined)
- 程序计数器是唯一一个在《java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。同时这个区域也没有GC
PC寄存器的使用举例
首先编写一个简单的java代码:
public class PCRegister {
public static void main(String[] args) {
int i = 10;
int j = 20;
int k = i+j;
}
}
然后对PCRegister.java进行编译,并使用javap -v PCRegister.class命令反解析成字节码文件,文件主要格式如下:
如上图:指令地址(偏移地址),也就是我们pc寄存器中存储的结构。我们的pc寄存器存储的是5这个指令地址,那么执行引擎执行时一方面会操作栈针的局部变量表、操作数栈…另一方面会根据pc寄存器的指令地址读取相对应的操作指令,将操作指令翻译成为机器指令,最后让cpu运算机器指令。
常见问题
为什么PC寄存器要被设置成线程私有的?
我们都知道所谓的多线程只是通过时间片轮询执行的,也就是说在某一时刻,一个处理器或一个cpu内核只能执行一条线程,此时其他的线程处于中断状态,而当轮询到中断线程时,就需要恢复到中断前的状态。那么如何保证为每条线程记录他的字节码指令地址呢?最好的办法就是为每一条线程都单独分配一个PC寄存器,这样一来,各个线程独立计算,不会出现相互干扰的情况。