JVM-執行時資料區之PC暫存器

niulongwei發表於2021-06-01

1.執行時資料區圖

執行時資料區
執行時資料區是在類載入完成後所經歷的階段,當我們通過前面的:類的載入 --> 驗證 --> 準備 --> 解析 --> 初始化,這幾個階段完成後,執行引擎就會對類進行使用,這時就用到了執行時資料區。

舉例來說,類的載入過程就好像是買菜的過程,經過一系列奔波,從購買到檢驗,最後再送到廚房(也就是執行時資料區)。而執行引擎就是一名廚師,他會用準備好的蔬菜去進行菜品的製作。

2.程式計數器(PC暫存器)

官方文件網址:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html

2.1 PC暫存器介紹

  • JVM中的程式計數暫存器(Program Counter Register)中,Register的命名源於CPU的暫存器,暫存器儲存指令相關的現場資訊。CPU只有把資料裝載到暫存器才能夠執行。
  • 這裡,並非是廣義上所指的物理暫存器,或許將其翻譯為PC計數器(或指令計數器)會更加貼切(也稱為程式鉤子),並且也不容易引起一些不必要的誤會。JVM中的PC暫存器是對物理PC暫存器的一種抽象模擬。
  • 它是一塊很小的記憶體空間,幾乎可以忽略不記。也是執行速度最快的儲存區域。
  • 在JVM規範中,每個執行緒都有它自己的程式計數器,是執行緒私有的,生命週期與執行緒的生命週期保持一致。
  • 任何時間一個執行緒都只有一個方法在執行,也就是所謂的當前方法。程式計數器會儲存當前執行緒正在執行的Java方法的JVM指令地址;或者,如果是在執行native方法,則是未指定值(undefned)。
  • 它是程式控制流的指示器,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成。
  • 位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令。
  • 它是唯一一個在Java虛擬機器規範中沒有規定任何OutofMemoryError情況的區域。

2.2 PC暫存器的作用

PC暫存器用來儲存指向下一條指令的地址,也即將要執行的指令程式碼。由執行引擎讀取下一條指令,並執行該指令。

 static {};
    descriptor: ()V
    flags: (0x0008) ACC_STATIC
    Code:
      stack=3, locals=0, args_size=0
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #3                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        10: invokestatic  #5                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        13: invokevirtual #6                  // Method java/lang/Thread.getName:()Ljava/lang/String;
        16: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: ldc           #8                  // String 初始化當前類
        21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        24: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        30: goto          30
      LineNumberTable:
        line 22: 0
        line 23: 30
      StackMapTable: number_of_entries = 1
        frame_type = 30 /* same */
}

上面位元組碼中左邊的數字代表指令地址(指令偏移),即 PC 暫存器中可能儲存的值,然後執行引擎讀取 PC 暫存器中的值,並執行該指令,中間的就是指令,後面的#+數字則是執行時常量池中的符號引用,具體後面會說。
在這裡插入圖片描述

面試題

1.使用PC暫存器儲存位元組碼指令地址有什麼用呢?

  • 因為CPU需要不停的切換各個執行緒,這時候切換回來以後,就得知道接著從哪開始繼續執行
  • JVM的位元組碼直譯器就需要通過改變PC暫存器的值來明確下一條應該執行什麼樣的位元組碼指令

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

  • 我們都知道所謂的多執行緒在一個特定的時間段內只會執行其中某一個執行緒的方法,CPU會不停地做任務切換,這樣必然導致經常中斷或恢復,如何保證分毫無差呢?為了能夠準確地記錄各個執行緒正在執行的當前位元組碼指令地址,最好的辦法自然是為每一個執行緒都分配一個PC暫存器,這樣一來各個執行緒之間便可以進行獨立計算,從而不會出現相互干擾的情況。
  • 由於CPU時間片輪限制,眾多執行緒在併發執行過程中,任何一個確定的時刻,一個處理器或者多核處理器中的一個核心,只會執行某個執行緒中的一條指令。
  • 這樣必然導致經常中斷或恢復,如何保證分毫無差呢?每個執行緒在建立後,都會產生自己的程式計數器和棧幀,程式計數器在各個執行緒之間互不影響。

擴充套件:

CPU 時間片

  • CPU時間片即CPU分配給各個程式的時間,每個執行緒被分配一個時間段,稱作它的時間片。
  • 在巨集觀上:我們可以同時開啟多個應用程式,每個程式並行不悖,同時執行。
  • 但在微觀上:由於只有一個CPU,一次只能處理程式要求的一部分,如何處理公平,一種方法就是引入時間片,每個程式輪流執行。

並行,併發

  • 並行是指多個處理器或者是多核的處理器同時處理多個不同的任務。
  • 而併發是指兩個或多個任務在同一時間間隔執行(執行緒來回切換)。

相關文章