每個JAVA開發人員都知道字節碼將由 JRE (Java運行時環境)執行。但是很多人不知道JRE是 Java Virtual machine (JVM)的實現,它分析字節碼、解釋代碼并執行代碼。作為開發者,了解JVM的體系結構非常重要,因為它使我們能夠更有效地編寫代碼。在本文中,我們將更深入地了解Java中的JVM體系結構和JVM的不同組件。
什么是JVM呢?
虛擬機是物理機的軟件實現。Java是用 WORA(編寫一次運行到任何地方) 的概念開發的,它在 VM 上運行。編譯器將Java文件 編譯 成Java .class文件,然后將.class文件輸入JVM, JVM加載并執行類文件。
JVM是如何工作的?
如圖所示,JVM分為三個主要子系統:
- 類加載器子系統
- 運行時數據區
- 執行引擎
1. 類加載器子系統
Java的 動態類加載功能 由類加載器子系統處理。它裝載的鏈接。在運行時而不是編譯時首次引用類時初始化類文件。
1.1 加載
類將由該組件加載。引導類加載器、擴展類加載器和應用程序類加載器是有助于實現這一目標的三個類加載器。
- 引導類加載器 – 負責從引導類路徑加載類,除了 rt.jar 什么也沒有。這個加載程序將獲得最高優先級。
- 擴展類加載器 – 負責加載ext文件夾 (jrelib) 中的類。
- 應用程序類加載器 –負責加載應用程序級類路徑、所述環境變量的路徑等。
上述類加載器在加載類文件時將遵循委托層次結構算法。
1.2 鏈接
- 驗證 – 字節碼驗證器將驗證生成的字節碼是否正確,如果驗證失敗,我們將得到驗證錯誤。
- 準備 – 內存將為所有靜態變量分配默認值。
- 解析 – 所有符號內存引用將被來自方法區域的原始引用所替換。
1.3 初始化
這是類加載的最后階段;在這里,所有 靜態變量 都將被賦初始值,并且 靜態塊 也會被執行。
2. 運行時數據區
運行時數據區被分為五個主要組件:
- 方法區– 所有類級數據都將存儲在這里,包括靜態變量。每個JVM只有一個方法區,它是資源共享的。
- 堆–所有對象及其對應的實例變量和數組都將存儲在這里。每個JVM也僅有一個堆。由于方法區和堆被多個線程共享內存,因此存儲的數據不是線程安全的。
- 棧–每個線程將創建一個單獨的運行時棧。每個方法調用都會在棧內存中生成一個條目,稱為棧幀。所有本地變量都將在棧內存中創建。棧區域是線程安全的,因為它不是內存共享的。
- 棧區域被分為三個部分:
- 局部變量數組 – 與方法相關,涉及到局部變量以及相應的值都將存儲在這里。
- 操作數堆棧 –如果需要執行任何中間操作,操作數堆棧充當運行時工作區來執行操作。
- 幀數據 – 所有與方法對應的符號都存儲在這里。在任何 異常 情況下,catch塊信息都將保存在幀數據中。
- PC寄存器– 每個線程將有單獨的PC寄存器,以保持當前執行指令的地址一旦指令執行,PC寄存器能順利地更新到下一條指令。
- 本地方法棧– 本機方法棧保存著本地方法信息。對于每個線程,都將創建一個單獨的本機方法棧。
3. 執行引擎
被分配給 運行時數據區 的字節碼將由執行引擎執行。執行引擎讀取字節碼并逐個執行。
- 解釋器– 解釋器更快地解釋字節碼,但執行速度很慢。解釋器的缺點是,當一個方法被多次調用時,每次都需要一個新的解釋。
- JIT編譯器
- – JIT編譯器消除了解釋器的缺點。執行引擎將在轉換字節碼時使用解釋器的幫助,但是當它發現重復的代碼時,它使用JIT編譯器,JIT編譯整個字節碼并將其更改為本機代碼。此本機代碼將直接用于重復的方法調用,從而提高系統的性能。
- 中間代碼生成器 – 生成中間代碼
- 代碼優化器 – 負責優化上面生成的中間代碼
- 目標代碼生成器 – 負責生成機器代碼或本地代碼
- 分析器 – 一個特殊的組件,負責尋找熱點,即方法是否被多次調用。
- 垃圾收集器:收集和刪除未引用的對象。可以通過調用 System.gc() 觸發垃圾收集,但不能保證執行。JVM的垃圾收集收集創建的對象。
Java本地接口(JNI): JNI將與本地方法庫交互,并提供執行引擎所需的本地庫。
本機方法庫: 這是執行引擎所需的本機庫的集合。