每一個Java開發人員都知道字節碼是由JRE(Java Runtime Environment)執行的。但是很少有人知道JRE是Java虛擬機(JVM)的實現,它分析字節碼,解釋代碼並執行。瞭解JVM的體系結構,對於作為一名開發人員來說是非常重要的。因為它使我們能夠更加高效地編寫代碼。在本文中,我們將更加深入瞭解Java中的JVM體系結構以及JVM的各種不同組件。
什麼是JVM
虛擬機是物理機器的軟件實現。Java的開發遵循WORA(一次編寫隨處運行)理念,它運行在VM(虛擬機)上。編譯器將Java文件編譯成Java.class文件,之後,將.class文件輸入到JVM中,加載並執行該類文件。下圖是JVM的體系結構:
JVM體系結構圖
JVM是如何工作的?
如上面的體系結構圖所示,JVM分為三個主要的子系統:
類加載器子系統
運行時數據區
執行引擎
1、類加載器子系統
Java的動態類加載功能是由類加載器子系統處理的。它負責加載、鏈接,並且在運行時首次引用類的時候初始化類,而不是在編譯期間。
1.1、加載
這個組件負責加載類。BootStrap類加載器、Extension類加載器和Application類加載器是實現這個功能的三大類加載器。
BootStrap類加載器—— 負責從classpath加載類,如果沒有類存在,將只加載rt.jar。這個加載器的優先級最高。
Extension類加載器 —— 負責加載擴展文件夾(jre\lib)中的類。
Application類加載器 —— 負責加載應用級classpath和環境變量指向的路徑下的類。
上述類加載器在加載類文件時遵循委託層次結構算法。
1.2、鏈接
校驗 —— 字節碼驗證器將校驗生成的字節碼是否正確,如果校驗失敗,我們將獲得校驗錯誤信息。
準備 —— 對於所有的靜態變量,內存將被申請並分配默認值。
解析 —— 所有標記的內存引用從方法區域被替換成的原始引用。
1.3、初始化
這是類加載的最後階段,所有的靜態變量都將被分配原值,靜態代碼塊將被執行。
2、運行時數據區
運行時數據區被劃分為五個主要部分:
方法區 —— 所有類級數據都將存儲在這裡,包括靜態變量。每一個JVM只有一個方法區,並且它是一個共享資源。
堆區 —— 所有對象及其對應的實例變量和數組等存儲在此,每個JVM同樣只有一個堆區。由於方法區和堆區是多線程內存共享,因此存儲的數據是非線程安全的。
棧區 —— 每個線程都會創建一個單獨的運行時棧。在每一次方法調用,都會在棧內存中創建一個棧幀(Stack Frame)。所有局部變量將在棧內存中創建。棧區是線程安全的,因為它不是一個共享資源。棧幀可以被劃分為三個實體:
局部變量數組 —— 與方法中有多少局部變量有關,相應的值將存儲在此處。
操作數棧 —— 如果任何的中間操作需要被執行,操作數棧將作為運行時工作區來執行操作。
幀數據 —— 與方法相對應的所有符號存儲在此。在任何異常情況下,catch塊的信息被保留在幀數據中。
PC寄存器 —— 每一個線程都有單獨的PC寄存器,一旦執行指令,PC寄存器將被下一條指令更新,保存當前執行指令的地址。
本地方法棧 —— 本地方法棧保存本地方法信息,每一個線程都會創建一個單獨的本地方棧。
3、執行引擎
分配到運行時數據區的字節碼將被執行引擎執行。執行引擎讀取字節碼並逐一執行。
解釋器 —— 解釋器能更加快速地解釋字節碼,但是執行緩慢。解釋器的缺點是當多次調用一個方法時,每次都要重新解釋。
JIT編譯器 —— JIT編譯器彌補瞭解釋器的不足。執行引擎使用解釋器來轉換字節碼,當它發現重複的代碼時,它將使用JIT編譯器來編譯整個字節碼並轉換為本地代碼。本地代碼將直接被重複的方法所調用,從而提高系統性能。
中間代碼生成器 —— 生成中間代碼。
代碼優化器 —— 負責優化上述生成的中間代碼。
目標代碼生成器 —— 負責生成機器碼或者本地代碼。
分析器 —— 一個特殊的組件,負責查找熱點代碼,比如一個方法是否被調用多次。
垃圾回收器 —— 回收並刪除未引用的對象。可以通過調用System.gc()來觸發垃圾回收,但不能保證它執行。JVM的垃圾回收是回收被創建的對象。
Java本地接口(JNI):JNI與本地方法庫交互,併為執行引擎提供本地方法庫。
本地方法庫(Native Method Libraries):它是執行引擎所需的本地庫集合。
英文原文:https://dzone.com/articles/jvm-architecture-explained