天天看點

Java記憶體區域之程式計數器--《深入了解Java虛拟機》學習筆記及個人了解(一)

深入了解Java虛拟機,JVM,記憶體區域,棧幀

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。

---------------------------------------------------------

學如不及,猶恐失之