天天看點

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

作者:一個即将被退役的碼農

開局上圖,繼續複習jvm整體架構

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

什麼是程式計數器(PC寄存器)

JVM中的程式計數寄存器(Program Counter Register),Register的命名源于CPU的寄存器,寄存器存儲指令相關的現場資訊,CPU隻有把資料裝載到寄存器才能夠運作。

  這裡的寄存器,并非是廣義上所指的實體寄存器,将其翻譯為PC計數器(或指令計數器)更為貼切一些(也稱為程式鈎子),并且也不容易引起一些不必要的誤會。JVM中的PC寄存器是對實體PC寄存器的一種抽象模拟。

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

PC 寄存器

  程式計數器是一塊很小的記憶體空間,幾乎可以忽略不記,卻是運作速度最快的存儲區域。

  在JVM規範中,每個線程都有它自己的程式計數器,是線程私有的,生命周期與線程的生命周期保持一緻。

  任何時間一個線程都隻有一個方法在執行,也就是所謂的目前方法。程式計數器會存儲目前線程正在執行的Java方法的JVM位元組碼指令位址;如果該方法執行的是Native方法,則程式計數器的值為空(Undefined)。

  它是程式控制流的訓示器,分支、循環、跳轉、異常處理、線程恢複等基礎功能都需要依賴這個計數器來完成。位元組碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令。

  它是唯一一個在Java虛拟機規範中沒有 Out Of Memory(記憶體溢出)情況的區域。

位元組碼的執行原理

在虛拟機的概念模型裡,位元組碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、循環、跳轉、異常處理、線程恢複等基礎功能都需要依賴這個計數器來完成。

概念不好了解?那麼通過代碼方式來看下,

java代碼:

public class PCRegister {
    public static void main(String[] args) {
    int x = 1;
    int y = 2;
    System.out.println(x+y);
    }
}           

為了了解其機制,首先,我們得檢視反編譯後的位元組碼檔案。

如何檢視?有以下兩種方式,

方式1(控制台指令):

右鍵位元組碼檔案打開控制台終端:

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

在控制台輸入指令:javap -v 位元組碼檔案名稱

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

紅框裡的内容即為位元組碼檔案反編譯後的内容

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

方式2(IDEA插件):

  1. 安裝 jclasslib Bytecode Viewer 插件
深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

2.打開位元組碼檔案 PCRegister.class -> 點選 View -> 點選 Show Bytecode with Jclasslib

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

3.選中方法 -> 選中main -> 選中 Code, 即可檢視位元組碼反編譯後的内容

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

分析:

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

如上圖,位元組碼檔案反編譯後可以看到有一系列 指令位址和 操作指令。

要想讓計算機執行程式,需要讓執行引擎中的解釋器将位元組碼操作指令解釋成CPU能夠識别的機器指令。

而選取哪一條操作指令進行解釋并執行,這個時候就需要依賴于程式計數器了。可以把它想象成一個臨時空間,用于存儲位元組碼操作指令的指令位址。

本圖中,0 就是一個指令位址,通過指令位址就能夠找到哪條指令,說明目前需要選取執行的操作指令是:iconst_1。

如果執行完0後,需要執行指向1的這條指令,那麼将程式計數器(PC計數器)中存儲的指令位址改成1就行了。

程式計數器的作用和特點?

PC寄存器用來存儲指向下一條指令的位址,也即将要執行的指令代碼。由執行引擎讀取下一條指令。

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器
  • 它是一塊很小的記憶體空間,幾乎可以忽略不記。也是運作速度最快的存儲區域。
  • 在JVM規範中,每個線程都有它自己的程式計數器,是線程私有的,生命周期與線程的生命周期保持一緻。
  • 任何時間一個線程都隻有一個方法在執行,也就是所謂的目前方法。程式計數器會存儲目前線程正在執行的Java方法的JVM指令位址;或者,如果是在執行native方法,則是未指定值(undefined)。
  • 它是程式控制流的訓示器,分支、循環、跳轉、異常處理、線程恢複等基礎功能都需要依賴這個計數器來完成。
  • 位元組碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令。
  • 它是唯一一個在Java虛拟機規範中沒有規定任何OutotMemoryError情況的區域。

程式計數器的面試問題?

1.為什麼使用PC寄存器記錄目前線程的執行位址呢?

因為CPU需要不停的切換各個線程,這時候切換回來以後,就得知道接着從哪開始繼續執行。

JVM的位元組碼解釋器就需要通過改變PC寄存器的值來明确下一條應該執行什麼樣的位元組碼指令。

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

2 PC寄存器為什麼被設定為私有的?

  • 我們都知道所謂的多線程在一個特定的時間段内隻會執行其中某一個線程的方法,CPU會不停地做任務切換,這樣必然導緻經常中斷或恢複,如何保證分毫無差呢?為了能夠準确地記錄各個線程正在執行的目前位元組碼指令位址,最好的辦法自然是為每一個線程都配置設定一個PC寄存器,這樣一來各個線程之間便可以進行獨立計算,進而不會出現互相幹擾的情況。
  • 由于CPU時間片輪限制,衆多線程在并發執行過程中,任何一個确定的時刻,一個處理器或者多核處理器中的一個核心,隻會執行某個線程中的一條指令。
  • 這樣必然導緻經常中斷或恢複,如何保證分毫無差呢?每個線程在建立後,都會産生自己的程式計數器和棧幀,程式計數器在各個線程之間互不影響。
深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

CPU時間片

  • CPU時間片即CPU配置設定給各個程式的時間,每個線程被配置設定一個時間段,稱作它的時間片。
  • 在宏觀上:俄們可以同時打開多個應用程式,每個程式并行不悖,同時運作。
  • 但在微觀上:由于隻有一個CPU,一次隻能處理程式要求的一部分,如何處理公平,一種方法就是引入時間片,每個程式輪流執行。

并行:vs 串行

并發:單核 交替執行多個線程的指令

深入了解JVM虛拟機——Java記憶體模型結構之程式計數器

繼續閱讀