程序計數器(關於java虛擬機內存的那些事)

什麼是程序計數器?

程序計數器是一塊 較小 的內存空間,它可以看做是當前線程所執行的字節碼的 行號指示器 ;在虛擬機的概念模型裏(僅僅是概念模型,各種虛擬機可能會通過一些更高效的方式去實現),字節碼解釋器工作時,就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳準、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成 ;
———–摘抄自 周志明版 《深入理解java虛擬機》 P39

簡單的理解爲,是程序計數器保證了程序的正常執行 ;

有什麼特點

  • 線程私有的
  • 是java虛擬機規範裏面, 唯一 一個 沒有規定任何 OutOfMemoryError 情況的區域
  • 生命週期隨着線程,線程啓動而產生,線程結束而消亡

爲什麼具有這些特點

這裏重點理解 :程序計數器,可以看做是當前線程執行的字節碼的 行號指示器 ,這句話;要理解這句話,需要先知道字節碼文件長什麼樣子,看下面的代碼

// java 文件被翻譯爲字節碼的時候,字節碼大概類似於下面的樣子
public void haha(){
// 原來的 haha 方法內部的 java 代碼,被翻譯爲下面的類似於彙編語言的指令
    0 xxxx ....
    2 xxxx ....
    4 xx  ...
    5 xxx ...
}

上面左邊的 0、2、4、5 ,就是類似於字節碼的行號(實際是指令的偏移地址),程序計數器中保存中的值,就是它們;字節碼解釋器,就是根據它們,來執行程序的 ;

理解了程序計數器,就好理解它的這些特點了;

我們都知道,java是支持多線程的,當CPU執行權從 A 線程,轉移到 B 線程的時候,JVM就要暫時掛起線程 A ,去執行線程 B ;當線程 A 再次得到CPU執行權的時候,又會掛起B線程,繼續執行 A 線程 ;

我們想象下,CPU是怎麼知道記住之前A線程,執行到哪一處的?
答案是,CPU根本就不會記住之前執行到哪裏了,它只是埋頭苦幹;那是什麼保證了切換線程的程序可以正常執行的;答案是 : 程序計數器 ;程序計數器裏面保存的是 當前線程執行的字節碼的行號(看着像行號,其實是指令地址);

那麼,我們需要幾個程序計數器呢?如果,我們只有一個的話,切換B線程以後,程序計數器裏面保存的就是B線程所執行的字節碼的行號了,再切換回A線程,就蒙圈了,不知道執行到哪裏了,因爲,程序計數器裏面保存的是B線程當前執行的字節碼地址 ;因此,我們可以想象出,要爲每個線程都分配一個程序計數器,因此,程序計數器的內存空間是線程私有的 ;這樣即使線程 A 被掛起,但是線程 A 裏面的程序計數器,記住了A線程當前執行到的字節碼的指令地址了 ,等再次切回到A線程的時候,看一下程序計數器,就知道之前執行到哪裏了!

那麼程序計數器,什麼時候分配內存呢?我們試想下,一個線程在執行的任何期間,都會失去CPU執行權,因此,我們要從一個線程被創建開始執行,就要無時無刻的記錄着該線程當前執行到哪裏了!因此,線程計數器,必須是線程被創建開始執行的時候,就要一同被創建;

程序計數器,保存的是當前執行的字節碼的偏移地址(也就是之前說的行號,其實那不是行號,是指令的偏移地址,只是爲了好理解,才說是行號的,),當執行到下一條指令的時候,改變的只是程序計數器中保存的地址,並不需要申請新的內存來保存新的指令地址;因此,永遠都不可能內存溢出的;因此,jvm虛擬機規範,也就沒有規定,也是唯一一個沒有規定 OutOfMemoryError 異常 的區域; ;

當線程執行的是本地方法的時候,程序計數器中保存的值是空(undefined);原因很簡單:本地方法是C++/C 寫的,由系統調用,根本不會產生字節碼文件,因此,程序計數器也就不會做任何記錄 ;

參考

筆者在讀書的時候,並未立馬理解程序計數器,看了下面的博文,纔有所理解),貼出鏈接,如果你恰巧讀到這裏,可以去看下,下文的作者是怎麼講的 ;

JVM程序計數器


作者:淮左白衣
來源:CSDN
原文:https://blog.csdn.net/youngyouth/article/details/79868299
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章