程序计数器
本文参考与《深入理解Java虚拟机》第三版,重复学习这部分知识,有助于后续其余知识理解。建议这部分知识阅读2遍以上。
程序计数器( Program Counter Register) 是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。字节码解释器工作的时候就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
计数器:也叫做PC寄存器,一个指定类大小的,且不会出现OOM的内存区域。
计数器的值存在以下两种可能:
- 执行 Java 方法的时候,记录的是正在执行的虚拟机字节码指令的地址。
- 如果是一个 Native 方法,那么计数器为空( Undefined)。
特点:
整个《Java虚拟机规范》中没有规定OOM的内存区域,因为只会存一个字节码指令的地址,内存小且固定,无论方法调用了多少,执行了多少,都只会存入一条。且随着线程的结束而销毁。
Q&A
-
为什么是线程私有的内容?
由于Java虚拟机的多线程是通过线程之间的轮换、分配处理器执行时间的方式来实现的,在任何一个时刻,一个处理器(对于多核的处理器来说是一个核)都只会执行一条线程中的指令。因此,为了线程切换过后能够恢复到原来正确的执行位置,每条线程都需要一个独立的程序计数器,每条线程之间各不影响,独立存储。
-
执行了 Native 方法过后,会出现计数器写入为空,那么线程切换过后,怎么能够恢复到执行前的状态?
以下内容借鉴于:https://www.zhihu.com/question/40598119/answer/87381512
这里的“pc寄存器”是在抽象的JVM层面上的概念——当执行Java方法时,这个抽象的“pc寄存器”存的是Java字节码的地址。实现上可能有两种形式,一种是相对该方法字节码开始处的偏移量,叫做bytecode index,简称bci;另一种是该Java字节码指令在内存里的地址,叫做bytecode pointer,简称bcp。
对native方法而言,它的方法体并不是由Java字节码构成的,自然无法应用上述的“Java字节码地址”的概念。所以JVM规范规定,如果当前执行的方法是native的,那么pc寄存器的值未定义——是什么值都可以。
上面是JVM规范所定义的抽象概念,那么实际实现呢?
Java线程总是需要以某种形式映射到OS线程上。映射模型可以是:- 1:1(原生线程模型)
- n:1(绿色线程 / 用户态线程模型)
- m:n(混合模型)。
以HotSpot VM的实现为例,它目前在大多数平台上都使用1:1模型,也就是每个Java线程都直接映射到一个OS线程上执行。此时,native方法就由原生平台直接执行,并不需要理会抽象的JVM层面上的“pc寄存器”概念——原生的CPU上真正的PC寄存器是怎样就是怎样。就像一个用C或C++写的多线程程序,它在线程切换的时候是怎样的,Java的native方法也就是怎样的。