天天看点

深入理解JVM虚拟机——Java内存模型结构之程序计数器

作者:一个即将被退役的码农

开局上图,继续复习jvm整体架构

深入理解JVM虚拟机——Java内存模型结构之程序计数器

什么是程序计数器(PC寄存器)

JVM中的程序计数寄存器(Program Counter Register),Register的命名源于CPU的寄存器,寄存器存储指令相关的现场信息,CPU只有把数据装载到寄存器才能够运行。

  这里的寄存器,并非是广义上所指的物理寄存器,将其翻译为PC计数器(或指令计数器)更为贴切一些(也称为程序钩子),并且也不容易引起一些不必要的误会。JVM中的PC寄存器是对物理PC寄存器的一种抽象模拟。

深入理解JVM虚拟机——Java内存模型结构之程序计数器

PC 寄存器

  程序计数器是一块很小的内存空间,几乎可以忽略不记,却是运行速度最快的存储区域。

  在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。

  任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM字节码指令地址;如果该方法执行的是Native方法,则程序计数器的值为空(Undefined)。

  它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

  它是唯一一个在Java虚拟机规范中没有 Out Of Memory(内存溢出)情况的区域。

字节码的执行原理

在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

概念不好理解?那么通过代码方式来看下,

java代码:

public class PCRegister {
    public static void main(String[] args) {
    int x = 1;
    int y = 2;
    System.out.println(x+y);
    }
}           

为了了解其机制,首先,我们得查看反编译后的字节码文件。

如何查看?有以下两种方式,

方式1(控制台命令):

右键字节码文件打开控制台终端:

深入理解JVM虚拟机——Java内存模型结构之程序计数器

在控制台输入命令:javap -v 字节码文件名称

深入理解JVM虚拟机——Java内存模型结构之程序计数器

红框里的内容即为字节码文件反编译后的内容

深入理解JVM虚拟机——Java内存模型结构之程序计数器

方式2(IDEA插件):

  1. 安装 jclasslib Bytecode Viewer 插件
深入理解JVM虚拟机——Java内存模型结构之程序计数器

2.打开字节码文件 PCRegister.class -> 点击 View -> 点击 Show Bytecode with Jclasslib

深入理解JVM虚拟机——Java内存模型结构之程序计数器

3.选中方法 -> 选中main -> 选中 Code, 即可查看字节码反编译后的内容

深入理解JVM虚拟机——Java内存模型结构之程序计数器

分析:

深入理解JVM虚拟机——Java内存模型结构之程序计数器

如上图,字节码文件反编译后可以看到有一系列 指令地址和 操作指令。

要想让计算机执行程序,需要让执行引擎中的解释器将字节码操作指令解释成CPU能够识别的机器指令。

而选取哪一条操作指令进行解释并执行,这个时候就需要依赖于程序计数器了。可以把它想象成一个临时空间,用于存储字节码操作指令的指令地址。

本图中,0 就是一个指令地址,通过指令地址就能够找到哪条指令,说明当前需要选取执行的操作指令是:iconst_1。

如果执行完0后,需要执行指向1的这条指令,那么将程序计数器(PC计数器)中存储的指令地址改成1就行了。

程序计数器的作用和特点?

PC寄存器用来存储指向下一条指令的地址,也即将要执行的指令代码。由执行引擎读取下一条指令。

深入理解JVM虚拟机——Java内存模型结构之程序计数器
  • 它是一块很小的内存空间,几乎可以忽略不记。也是运行速度最快的存储区域。
  • 在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。
  • 任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址;或者,如果是在执行native方法,则是未指定值(undefined)。
  • 它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
  • 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
  • 它是唯一一个在Java虚拟机规范中没有规定任何OutotMemoryError情况的区域。

程序计数器的面试问题?

1.为什么使用PC寄存器记录当前线程的执行地址呢?

因为CPU需要不停的切换各个线程,这时候切换回来以后,就得知道接着从哪开始继续执行。

JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。

深入理解JVM虚拟机——Java内存模型结构之程序计数器

2 PC寄存器为什么被设定为私有的?

  • 我们都知道所谓的多线程在一个特定的时间段内只会执行其中某一个线程的方法,CPU会不停地做任务切换,这样必然导致经常中断或恢复,如何保证分毫无差呢?为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每一个线程都分配一个PC寄存器,这样一来各个线程之间便可以进行独立计算,从而不会出现相互干扰的情况。
  • 由于CPU时间片轮限制,众多线程在并发执行过程中,任何一个确定的时刻,一个处理器或者多核处理器中的一个内核,只会执行某个线程中的一条指令。
  • 这样必然导致经常中断或恢复,如何保证分毫无差呢?每个线程在创建后,都会产生自己的程序计数器和栈帧,程序计数器在各个线程之间互不影响。
深入理解JVM虚拟机——Java内存模型结构之程序计数器

CPU时间片

  • CPU时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片。
  • 在宏观上:俄们可以同时打开多个应用程序,每个程序并行不悖,同时运行。
  • 但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。

并行:vs 串行

并发:单核 交替执行多个线程的命令

深入理解JVM虚拟机——Java内存模型结构之程序计数器

继续阅读