目录
概述
字节码取指令举例
CPU时间片
经典问题
使用PC寄存器存储字节码指令地址有什么用呢?
为什么使用PC寄存器记录当前线程的执行地址呢?
概述
运行时数据区中运行速度最快的存储区域,并且是线程私有的,每一个线程都具有自己的程序计数器,生命周期与线程的生命周期保持一致。
程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在Java虛拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的(也就是时间片分配),在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。 因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有“的内存。如果线程正在执行的是一个Java方法, 这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native) 方法,这个计数器值则应为空(Undefined) 。此内存区域是唯一一个 在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。(出自《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》)
字节码取指令举例
public class Main {
public static void main(String[] args) {
int a = 1;
int b = 3;
int c = a + b;
System.out.println(c);
}
}
上面这一段简单的代码对应的字节码文件如下:
0 iconst_1
1 istore_1
2 iconst_3
3 istore_2
4 iload_1
5 iload_2
6 iadd
7 istore_3
8 getstatic #2 <java/lang/System.out : Ljava/io/PrintStream;>
11 iload_3
12 invokevirtual #3 <java/io/PrintStream.println : (I)V>
15 return
而程序计数器所做的事情就是取上面的指令并执行。
CPU时间片
CPU时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片。
在宏观上:俄们可以同时打开多个应用程序,每个程序并行不悖,同时运行。
但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。
经典问题
使用PC寄存器存储字节码指令地址有什么用呢?
为什么使用PC寄存器记录当前线程的执行地址呢?
在本文的概述中已经讲到了Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的(也就是时间片分配),那么线程之间切换后如何保证再次回到该线程时继续执行接下来的指令呢?为了能够准确的记录各个线程正在执行的当前字节码的指令地址,最好的办法自然是为每一个线程都分配一个程序计数器。
说到这里其实又出现了一个问题,那就是程序执行的位置,这里我们只是取了指令,那么执行指令也需要一个线程私有的地方,不然也会出现和上述类似的问题。这个地方就是虚拟机栈,每一个线程都分配一个虚拟机栈,执行各自线程的任务。这样就解决了上述的问题。