JVM程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。

在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
                                             ------ 摘自《深入理解JAVA虚拟机》

  • 特点:
  1. 线程私有(也叫线程隔离性)。即每个线程工作时都有属于自己的独立计数器
  2. 执行java方法时,程序计数器是有值的,且记录的是正在执行的字节码指令的地址
  3. JVM规范中唯一没有规定OutOfMemoryError情况的区域
  4. 如果正在执行的是Native 方法,则这个计数器值为空
  5. 程序计数器占用内存很小,在进行JVM内存计算时,可以忽略不计。
  • 首先,为什么是线程私有?

Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现。也就是说,在同一时刻一个处理器内核只会执行一条线程,处理器切换线程时并不会记录上一个线程执行到哪个位置,所以为了线程切换后依然能恢复到原位,每条线程都需要有各自独立的程序计数器。

  • 为什么没有规定OutOfMemoryError?

如上文,程序计数器存储的是字节码文件的行号,而这个范围是可知晓的,在一开始分配内存时就可以分配一个绝对不会溢出的内存。

  • 为什么执行Native方法,值为空?

Native方法大多是通过C实现并未编译成需要执行的字节码指令,也就不需要去存储字节码文件的行号了。

因为native方法是java通过JNI直接调用本地C/C++库,可以近似的认为native方法相当于C/C++暴露给java的一个接口,java通过调用这个接口从而调用到C/C++方法。由于该方法是通过C/C++而不是java进行实现。那么自然无法产生相应的字节码,并且C/C++执行时的内存分配是由自己语言决定的,而不是由JVM决定的。

  • 如果执行Native方法时,程序计数器不存储值,那么线程切换后如何恢复原位继续执行?

这里的“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方法也就是怎样的。


原文链接:https://blog.csdn.net/jiangyang100/article/details/90614280

同时参考:https://www.cnblogs.com/manayi/p/9290490.html

原文地址:https://www.cnblogs.com/gslgb/p/13285158.html