1.運行時數據區圖

運行時數據區是在類加載完成后所經歷的階段,當我們通過前面的:類的加載 --> 驗證 --> 准備 --> 解析 --> 初始化,這幾個階段完成后,執行引擎就會對類進行使用,這時就用到了運行時數據區。
舉例來說,類的加載過程就好像是買菜的過程,經過一系列奔波,從購買到檢驗,最后再送到廚房(也就是運行時數據區)。而執行引擎就是一名廚師,他會用准備好的蔬菜去進行菜品的制作。
官方文檔網址:https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
- JVM中的程序計數寄存器(Program Counter Register)中,Register的命名源於CPU的寄存器,寄存器存儲指令相關的現場信息。CPU只有把數據裝載到寄存器才能夠運行。
- 這里,並非是廣義上所指的物理寄存器,或許將其翻譯為PC計數器(或指令計數器)會更加貼切(也稱為程序鈎子),並且也不容易引起一些不必要的誤會。JVM中的PC寄存器是對物理PC寄存器的一種抽象模擬。
- 它是一塊很小的內存空間,幾乎可以忽略不記。也是運行速度最快的存儲區域。
- 在JVM規范中,每個線程都有它自己的程序計數器,是線程私有的,生命周期與線程的生命周期保持一致。
- 任何時間一個線程都只有一個方法在執行,也就是所謂的當前方法。程序計數器會存儲當前線程正在執行的Java方法的JVM指令地址;或者,如果是在執行native方法,則是未指定值(undefned)。
- 它是程序控制流的指示器,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。
- 字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令。
- 它是唯一一個在Java虛擬機規范中沒有規定任何OutofMemoryError情況的區域。
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,一次只能處理程序要求的一部分,如何處理公平,一種方法就是引入時間片,每個程序輪流執行。
- 並行是指多個處理器或者是多核的處理器同時處理多個不同的任務。
- 而並發是指兩個或多個任務在同一時間間隔執行(線程來回切換)。
