1. 程式人生 > 實用技巧 >記憶體結構篇:程式計數器

記憶體結構篇:程式計數器

參考:https://www.cnblogs.com/newAndHui/p/11878504.html

一、定義

Program Counter Register 程式計數器(暫存器)(執行緒獨享):程式計數器是一塊 較小 的記憶體空間,它可以看做是當前執行緒所執行的位元組碼的 行號指示器 ;在虛擬機器的概念模型裡(僅僅是概念模型,各種虛擬機器可能會通過一些更高效的方式去實現),位元組碼直譯器工作時,就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、迴圈、跳準、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成 ;

  • 作用:記錄正在執行的虛擬機器位元組碼指令的地址(如果正在執行的是本地方法則為空
  • 特點:
    • 是執行緒私有的
    • 不會存在記憶體溢位
    • 生命週期隨著執行緒,執行緒啟動而產生,執行緒結束而消亡

二、特點詳解

理解:程式計數器,可以看做是當前執行緒執行的位元組碼的 行號指示器

//當java 檔案被翻譯為位元組碼的時候,位元組碼大概類似於下面的樣子
public void test() {
    0 xxxx ..
    2 xxxx ..
    4 xxxx ..
    5 xxxx ..
}

上面左邊的0、2、4、5,就是類似於位元組碼的行號(實際是指令的偏移地址),程式計數器中儲存的值就是它們;位元組碼直譯器,就是根據它們來執行程式的。

三、深入瞭解

java是支援多執行緒的,當CPU執行權從 A 執行緒,轉移到 B 執行緒的時候,JVM就要暫時掛起執行緒 A ,去執行執行緒 B ;當執行緒 A 再次得到CPU執行權的時候,又會掛起B執行緒,繼續執行 A 執行緒 ;

1、CPU是怎麼記住之前的A執行緒,執行到哪一處的:

CPU根本就不會記住之前執行到哪裡了,它只是埋頭苦幹;

2、那是什麼保證了切換執行緒的程式可以正常執行的:

程式計數器 ;程式計數器裡面儲存的是 當前執行緒執行的位元組碼的行號(看著像行號,其實是指令地址);

3、那麼,我們需要幾個程式計數器呢:每個執行緒都需要有一個獨立的程式計數器

如果只有一個的話,切換B執行緒以後,程式計數器裡面儲存的就是B執行緒所執行的位元組碼的行號了,再切換回A執行緒,就蒙圈了,不知道執行到哪裡了,因為,程式計數器裡面儲存的是B執行緒當前執行的位元組碼地址 ;因此,我們可以想象出,要為每個執行緒都分配一個程式計數器

因此,程式計數器的記憶體空間是執行緒私有的 ;這樣即使執行緒 A 被掛起,但是執行緒 A 裡面的程式計數器,記住了A執行緒當前執行到的位元組碼的指令地址了 ,等再次切回到A執行緒的時候,看一下程式計數器,就知道之前執行到哪裡了

4、那麼程式計數器,什麼時候分配記憶體呢:

一個執行緒在執行的任何期間,都會失去CPU執行權,因此,我們要從一個執行緒被建立開始執行,就要無時無刻的記錄著該執行緒當前執行到哪裡了!因此,執行緒計數器,必須是執行緒被建立開始執行的時候,就要一同被建立

5、程式計數器為什麼不會存在記憶體溢位:

程式技術器儲存的是當前執行的位元組碼的偏移地址,當執行到下一條指令的時候,改變的只是程式計數器中儲存的地址,並不需要申請新的記憶體來儲存新的指令地址,因此,永遠都不可能記憶體溢位;因此,jvm虛擬機器規範,也就沒有規定,也是唯一一個沒有規定 OutOfMemoryError 異常 的區域(即在同一塊記憶體上更改位元組碼的偏移地址,不需要再建立新的記憶體來儲存

6、為什麼當執行緒執行的是本地方法的時候,程式計數器中儲存的值是空:

因為本地方法是 C++/C 寫的,由系統呼叫,根本不會產生位元組碼檔案,因此,程式計數器也就不會做任何記錄