JVM體系結構詳解

  • 2019 年 10 月 7 日
  • 筆記

作者 | 康仔

每個Java開發人員都知道位元組碼將由JRE (Java運行時環境)執行。但是很多人不知道JRE是Java Virtual Machine(JVM)的實現,它分析位元組碼、解釋程式碼並執行程式碼。作為開發者,了解JVM的體系結構非常重要,因為它使我們能夠更有效地編寫程式碼。在本文中,我們將更深入地了解Java中的JVM體系結構和JVM的不同組件。

什麼是JVM呢?

虛擬機是物理機的軟體實現。Java是用WORA(編寫一次運行到任何地方)的概念開發的,它在VM上運行。編譯器將Java文件編譯成Java .class文件,然後將.class文件輸入JVM, JVM載入並執行類文件。下面是JVM的架構圖。

file

JVM是如何工作的?

如圖所示,JVM分為三個主要子系統:

  1. 類載入器子系統
  2. 運行時數據區
  3. 執行引擎

1. 類載入器子系統

Java的動態類載入功能由類載入器子系統處理。它裝載的鏈接。在運行時而不是編譯時首次引用類時初始化類文件。

1.1 載入

類將由該組件載入。引導類載入器、擴展類載入器和應用程式類載入器是有助於實現這一目標的三個類載入器。

  1. 引導類載入器 – 負責從引導類路徑載入類,除了rt.jar什麼也沒有。這個載入程式將獲得最高優先順序。
  2. 擴展類載入器 – 負責載入ext文件夾**(jrelib)**中的類。
  3. 應用程式類載入器 –負責載入應用程式級類路徑、所述環境變數的路徑等。

上述類載入器在載入類文件時將遵循委託層次結構演算法。

1.2 鏈接

  1. 驗證 – 位元組碼驗證器將驗證生成的位元組碼是否正確,如果驗證失敗,我們將得到驗證錯誤。
  2. 準備 – 記憶體將為所有靜態變數分配默認值。
  3. 解析 – 所有符號記憶體引用將被來自方法區域的原始引用所替換。

1.3 初始化

這是類載入的最後階段;在這裡,所有靜態變數都將被賦初始值,並且靜態塊也會被執行。

2. 運行時數據區

運行時數據區被分為五個主要組件:

  1. 方法區 – 所有類級數據都將存儲在這裡,包括靜態變數。每個JVM只有一個方法區,它是資源共享的。
  2. –所有對象及其對應的實例變數和數組都將存儲在這裡。每個JVM也僅有一個堆。由於方法區和堆被多個執行緒共享記憶體,因此存儲的數據不是執行緒安全的。
  3. –每個執行緒將創建一個單獨的運行時棧。每個方法調用都會在棧記憶體中生成一個條目,稱為棧幀。所有本地變數都將在棧記憶體中創建。棧區域是執行緒安全的,因為它不是記憶體共享的。 棧區域被分為三個部分:
    1. 局部變數數組 – 與方法相關,涉及到局部變數以及相應的值都將存儲在這裡。
    2. 操作數堆棧 –如果需要執行任何中間操作,操作數堆棧充當運行時工作區來執行操作。
    3. 幀數據 – 所有與方法對應的符號都存儲在這裡。在任何異常情況下,catch塊資訊都將保存在幀數據中。
  4. PC暫存器 – 每個執行緒將有單獨的PC暫存器,以保持當前執行指令的地址一旦指令執行,PC暫存器能順利地更新到下一條指令。
  5. 本地方法棧 – 本機方法棧保存著本地方法資訊。對於每個執行緒,都將創建一個單獨的本機方法棧。

3. 執行引擎

被分配給運行時數據區的位元組碼將由執行引擎執行。執行引擎讀取位元組碼並逐個執行。

  1. 解釋器 – 解釋器更快地解釋位元組碼,但執行速度很慢。解釋器的缺點是,當一個方法被多次調用時,每次都需要一個新的解釋。
  2. JIT編譯器 – JIT編譯器消除了解釋器的缺點。執行引擎將在轉換位元組碼時使用解釋器的幫助,但是當它發現重複的程式碼時,它使用JIT編譯器,JIT編譯整個位元組碼並將其更改為本機程式碼。此本機程式碼將直接用於重複的方法調用,從而提高系統的性能。
    1. 中間程式碼生成器 – 生成中間程式碼
    2. 程式碼優化器 – 負責優化上面生成的中間程式碼
    3. 目標程式碼生成器 – 負責生成機器程式碼或本地程式碼
    4. 分析器 – 一個特殊的組件,負責尋找熱點,即方法是否被多次調用。
  3. 垃圾收集器:收集和刪除未引用的對象。可以通過調用 System.gc()觸發垃圾收集,但不能保證執行。JVM的垃圾收集收集創建的對象。

Java本地介面(JNI): JNI將與本地方法庫交互,並提供執行引擎所需的本地庫。

本機方法庫: 這是執行引擎所需的本機庫的集合。