JVM 运行时内存空间详解——程序计数器
Posted 格子衫111
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM 运行时内存空间详解——程序计数器相关的知识,希望对你有一定的参考价值。
通过上一篇文章,我们大体了解了JVM的整体架构,其分为:元数据(JDK7是方法区)、堆、虚拟机栈、本地方法栈、程序计数器几个部分。
本篇文章,咱们对程序计数器进行剖析,一探究竟。
一、什么是程序计数器(PC寄存器)
程序计数器(Program Counter Register): 也叫PC寄存器,是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。
在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
概念不好理解?那么通过代码方式来看下,
java代码:
public class PCRegister {
public static void main(String[] args) {
int x = 1;
int y = 2;
System.out.println(x+y);
}
}
为了了解其机制,首先,我们得查看反编译后的字节码文件。
如何查看?有以下两种方式,
方式1(控制台命令):
- 右键字节码文件打开控制台终端:
- 在控制台输入命令:javap -v 字节码文件名称
- 红框里的内容即为字节码文件反编译后的内容
方式2(IDEA插件):
- 安装 jclasslib Bytecode Viewer 插件
2.打开字节码文件 PCRegister.class -> 点击 View -> 点击 Show Bytecode with Jclasslib
- 选中方法 -> 选中main -> 选中 Code, 即可查看字节码反编译后的内容
分析:
如上图,字节码文件反编译后可以看到有一系列 指令地址和 操作指令。
要想让计算机执行程序,需要让执行引擎中的解释器将字节码操作指令解释成CPU能够识别的机器指令。
而选取哪一条操作指令进行解释并执行,这个时候就需要依赖于程序计数器了。可以把它想象成一个临时空间,用于存储字节码操作指令的指令地址。
本图中,0 就是一个指令地址,通过指令地址就能够找到哪条指令,说明当前需要选取执行的操作指令是:iconst_1。
如果执行完0后,需要执行指向1的这条指令,那么将程序计数器(PC计数器)中存储的指令地址改成1就行了。
二、PC寄存器有哪些特点
-
区别于计算机硬件的pc寄存器,两者不略有不同。计算机用pc寄存器来存放“伪指令”或地址,而相对于虚拟机,pc寄存器它表现为一块内存,虚拟机的pc寄存器的功能也是存放伪指令,更确切的说存放的是将要执行指令的地址。
-
当虚拟机正在执行的方法是一个本地(native)方法的时候,jvm的pc寄存器存储的值是undefined。
-
程序计数器是线程私有的,它的生命周期与线程相同,每个线程都有一个。
-
此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError 情况的区域。
三、多个线程,如何确定执行到某个位置进行恢复呢
- Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。
- 因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存。
下面,举个栗子:
如上图所示,目前有两个线程,因为程序计数器线程私有,所以两个线程中各有一个程序计数器。
当Thread1 执行完指令2后,此时程序计数器保存的指令地址是3。此时时间片刚好切到 Thread2, Thread2开始执行,执行完指令6后,程序计数器的指令地址保存为7。
此时时间片再次切回 Thread1 , Thread1直接找到原来存储的指令3进行执行。
同理,后续如果切回Thread2线程,Thread2会找到原本存储的指令7进行执行。
这就是线程执行到某个位置的恢复,主要是依据程序计数器的值来实现的。
以上是关于JVM 运行时内存空间详解——程序计数器的主要内容,如果未能解决你的问题,请参考以下文章