JVM內幕:Java虛擬機器詳解,jvm內幕java虛擬機器
這篇文章解釋了JAVA 虛擬機器(JVM)的內部架構。顯示了遵守 Java SE 7 規範的典型的 JVM 核心內部組件。
顯示的組件分兩個章節解釋。第一章討論針對每個線程建立的組件,第二章節討論了線程無關組件。
- 線程
- JVM 系統線程
- 每個線程相關的
- 程式計數器
- 棧
- 本地棧
- 棧限制
- 棧幀
- 局部變數數組
- 運算元棧
- 動態連結
- 線程共用
- 堆
- 記憶體管理
- 非堆記憶體
- 即時編譯
- 方法區
- 類檔案結構
- 類載入器
- 更快的類載入
- 方法區在哪裡
- 類載入器參考
- 運行時常量池
- 異常表
- 符號表
- Interned 字串
線程
這裡所說的線程指程式執行過程中的一個線程實體。JVM 允許一個應用並發執行多個線程。Hotspot JVM 中的 Java 線程與原生作業系統線程有直接的映射關係。當執行緒區域儲存、緩衝區分配、同步對象、棧、程式計數器等準備好以後,就會建立一個作業系統原生線程。Java 線程結束,原生線程隨之被回收。作業系統負責調度所有線程,並把它們分配到任何可用的 CPU 上。當原生線程初始化完畢,就會調用 Java 線程的 run() 方法。run() 返回時,被處理未捕獲異常,原生線程將確認由於它的結束是否要終止 JVM 進程(比如這個線程是最後一個非守護線程)。當線程結束時,會釋放原生線程和 Java 線程的所有資源。
JVM 系統線程
如果使用 jconsole 或者其它調試器,你會看到很多線程在後台運行。這些後台線程與觸發 public static void main(String[]) 函數的主線程以及主線程建立的其他線程一起運行。Hotspot JVM 後台啟動並執行系統線程主要有下面幾個:
| 虛擬機器線程(VM thread) |
這個線程等待 JVM 到達安全點操作出現。這些操作必須要在獨立的線程裡執行,因為當堆修改無法進行時,線程都需要 JVM 位於安全點。這些操作的類型有:stop-the-world 記憶體回收、線程棧 dump、線程暫停、線程偏向鎖(biased locking)解除。 |
| 週期性任務線程 |
這線程負責定時器事件(也就是中斷),用來調度周期性操作的執行。 |
| GC 線程 |
這些線程支援 JVM 中不同的記憶體回收活動。 |
| 編譯器線程 |
這些線程在運行時將位元組碼動態編譯成本地平台相關的機器碼。 |
| 訊號分發線程 |
這個線程接收發送到 JVM 的訊號並調用適當的 JVM 方法處理。 |
線程相關組件
每個啟動並執行線程都包含下面這些組件:
程式計數器(PC)
PC 指當前指令(或作業碼)的地址,本地指令除外。如果當前方法是 native 方法,那麼PC 的值為 undefined。所有的 CPU 都有一個 PC,典型狀態下,每執行一條指令 PC 都會自增,因此 PC 儲存了指向下一條要被執行的指令地址。JVM 用 PC 來跟蹤指令執行的位置,PC 將實際上是指向方法區(Method Area)的一個記憶體位址。
棧(Stack)
每個線程擁有自己的棧,棧包含每個方法執行的棧幀。棧是一個後進先出(LIFO)的資料結構,因此當前執行的方法在棧的頂部。每次方法調用時,一個新的棧幀建立並壓棧到棧頂。當方法正常返回或拋出未捕獲的異常時,棧幀就會出棧。除了棧幀的壓棧和出棧,棧不能被直接操作。所以可以在堆上分配棧幀,並且不需要連續記憶體。
Native棧
並非所有的 JVM 實現都支援本地(native)方法,那些提供支援的 JVM 一般都會為每個線程建立本地方法棧。如果 JVM 用 C-linkage 模型實現 JNI(Java Native Invocation),那麼本地棧就是一個 C 的棧。在這種情況下,本地方法棧的參數順序、傳回值和典型的 C 程式相同。本地方法一般來說可以(依賴 JVM 的實現)反過來調用 JVM 中的 Java 方法。這種 native 方法調用 Java 會發生在棧(一般是 Java 棧)上;線程將離開本地方法棧,並在 Java 棧上開闢一個新的棧幀。
棧的限制
棧可以是動態分配也可以固定大小。如果線程請求一個超過允許範圍的空間,就會拋出一個StackOverflowError。如果線程需要一個新的棧幀,但是沒有足夠的記憶體可以分配,就會拋出一個 OutOfMemoryError。
棧幀(Frame)
每次方法調用都會建立一個新的棧幀並把它壓棧到棧頂。當方法正常返回或者調用過程中拋出未捕獲的異常時,棧幀將出棧。