Skip to content

TIP

相信学过计算机组成原理的人都知道,CPU内部的寄存器中就包含一个程序计数器,存放程序执行的下一条指令地址。程序每次执行时,CPU都会自动存储下一条指令的地址,以便下次继续执行。

JVM也有类似设定,它参考了CPU的程序计数器,自己设计了一个程序计数器,程序计数器记录当前线程正在执行的字节码的地址(在执行当前字节码程序开始时记录的下一条指定地址,也就是录当前线程正在执行的字节码的地址),程序计数器是线程隔离的,每一个线程在工作的时候都有一个独立的计数器。

为什么要存储这个字节码指令地址呢?

CPU的上下文切换会导致线程不停的切换,切换会引发线程的中断和恢复,中断时要记录当前线程执行的位置,恢复时从中断的位置继续执行。

为了达到这样一个效果, JVM为每条线程都分配一个程序计数器;这样每条线程都可以独立计算,当出现CPU上下文切换时,线程中断由程序计数器记录字节码指令位置,线程恢复就从中断位置继续执行。

举个例子,CPU并发2条线程:

  1. CPU首先执行线程A,假设线程A执行到第3行时,CPU突然切换执行线程B;
  2. 此时线程A就处于中断状态,线程A的计数器记录下个字节码地址为4;
  3. 当线程B执行到第10行时, CPU突然切换执行线程A,同理线程B中断, 并线程B记录下一个字节码地址11;
  4. 被切换的线程A恢复后,从字节码地址4开始执行。

总结:程序计数器的特点

  1. 每条线程都有自己的程序计数器,而且是线程私有的,它的生命周期与线程相同(随线程而生,随线程而灭)
  2. 程序控制流的指示器,例如:分支、循环、跳转、异常处理、线程恢复等基础功能都是依赖这个计数器来执行的。
  3. 字节码解析器的工作原理是通过计数器的值来执行下一条字节码指令的。
  4. 程序计数器具有线程隔离性
  5. 程序计数器占用的内存空间非常小,可以忽略不计
  6. 程序计数器是java虚拟机规范中唯一一个没有规定任何OutofMemeryError的区域
  7. 执行native本地方法时, 程序计数器的值为空。原因是native方法是java通过jni调用本地C/C+ +库来实现,非java字节码实现,所以无法统计

面试题思考

  1. 简单讲下什么是程序计数器?
  2. 程序计数器为什么要存储字节码指令地址呢?
  3. 为什么程序计数器具有线程隔离性?
  4. 为什么程序计数器是java虚拟机规范中唯一一个没有规定任何OutofMemeryError的区域?
    • 程序计数器是一块较小的内存区域,其主要作用是保存当前线程执行的字节码指令的地址,也就是下条即将执行的指令地址。因为程序计数器只保存线程的执行位置,并不会存储任何对象或数据,所以不会导致OutOfMemoryError。