Java内存区域之程序计数器--《深入理解Java虚拟机》学习笔记及个人理解(一)

时间:2022-12-13 06:32:43

Java虚拟机程序计数器

在书上的P39页

程序计数器干嘛的?

有了它,字节码解释器才可以知道下一条要执行的字节码指令是哪个。

无论是取下一条指令还是分支、循环、跳转、中断、线程恢复,都需要这个程序计数器。

程序计数器存在哪?

虚拟机区别于硬件,组成原理里学的程序计数器是用CS和IP寄存器来存,来表示指令地址。

而Java把程序计数器存在内存里。

我好像在操作系统中也听说过程序计数器?

是跟操作系统中的程序计数器有点类似。

在一个时刻,一个处理器只会执行一条线程,HotSpot中使用原生线程模型,OS线程和Java线程1:1映射。

操作系统中,当多个线程轮流切换着执行的时候,每个线程都需要记录自己被中断的位置,以方便恢复线程后能够继续接着执行。

参考操作系统中的线程控制块(TCB),当线程切换时要保存上下文状态,其中就包括指令计数器PC,线程被中断后再切换回来时就知道了从哪里开始继续执行。

Java中也一样,当有多个线程执行时,也是线程间进行轮转,当线程切换回来的时候就需要程序计数器来为该线程指明从哪里开始执行。

所以也就很好理解,Java的程序计数器是‘线程私有的’,每个线程都要有一个。

我听说程序计数器还可以为空?

Java在执行Native方法时,不是Java在工作,而是调用了操作系统中的方法,所以此时不需要Java的程序计数器,此时为空。

既然程序计数器此时为空,Native方法执行完了以后,该回到哪里呢?

我之前也对这个有疑问,就去查了查Java栈帧有关的知识...我是没查到什么。

就去看了c语言的一个栈帧教程,结合着汇编代码:http://ms.csdn.net/geek/187200

如果不想看,我就简单介绍一下:

汇编是这样做的:当一个方法调用一个方法时,用栈来保存上下文(调用一个函数前先push,最后方法结束时再pop出来),既然保存了上下文,就可以先保存调用者的pc指针,然后执行完方法后,再把pc还原,就可以回到上一层方法了。

Java每个方法执行的时候,都会在Java虚拟机栈创建一个栈帧。一个方法对应一个栈帧。Java控制不了寄存器,但是应该可以用内部堆栈来保存这个上下文,当方法执行完后,调整PC指针,让PC指向方法调用指令后面的一条指令。

JVMS7中的2.6.4 Normal Method Invocation Completion(方法正常调用完成)中写道:

The current frame (§2.6) is used in this case to restore the state of the invoker, including its local variables and operand stack, with the program counter of the invoker appropriately incremented to skip past the method invocation instruction. Execution then continues normally in the invoking method's frame with the returned value (if any) pushed onto the operand stack of that frame.

手动翻译:在这种情况,当前栈桢就被用来恢复调用者的状态,都恢复哪些呢?恢复局部变量表、操作数栈 和 程序计数器(pc指针),而这个程序计数器要适当地增加,来指向下一条指令(也就是调用函数的下一句)。使调用者方法能够正常地继续执行下去,而且返回值push到了调用方法的操作数栈中。

Native方法也是方法,当他被调用了后,会进入本地方法栈。有的虚拟机会把虚拟机栈和本地方法栈合为一个。当Native方法在本地方法栈里执行完时,也会根据类似的机制让PC重新指向调用指令后面的一条指令。

这个博客里的本地方法栈的图很形象:https://www.cnblogs.com/wade-luffy/p/5813747.html