JVM架构解析
本文闡述了JVM的構成和組件,配圖清晰易懂,是學習Java開發者的入門必讀文章。
每個Java開發人員都知道字節碼經由JRE(Java運行時環境)執行。但他們或許不知道JRE其實是由Java虛擬機(JVM)實現,JVM分析字節碼,解釋并執行它。作為開發人員,了解JVM的架構是非常重要的,因為它使我們能夠編寫出更高效的代碼。本文中,我們將深入了解Java中的JVM架構和JVM的各個組件。
JVM
虛擬機是物理機的軟件實現。Java的設計理念是WORA(Write Once Run Anywhere,一次編寫隨處運行)。編譯器將Java文件編譯為Java .class文件,然后將.class文件輸入到JVM中,JVM執行類文件的加載和執行的操作。請看以下的JVM架構圖:
?
?
JVM是如何工作的?
如上面架構圖所示,JVM分為三個主要子系統:
- 類加載器子系統(Class Loader Subsystem)
- 運行時數據區(Runtime Data Area)
- 執行引擎(Execution Engine)
1. 類加載器子系統
Java的動態類加載功能由類加載器子系統處理,處理過程包括加載和鏈接,并在類文件運行時,首次引用類時就開始實例化類文件,而不是在編譯時進行。
1.1 加載
Boot Strap類加載器,Extension類加載器和Application(類加載器是實現類加載過程的三個類加載器。
(1) Boot Strap類加載器:負責從引導類路徑加載類,除了rt.jar,它具有最高優先級;
(2) Extension 類加載器:負責加載ext文件夾(jre lib)中的類;
(3) Application類加載器:負責加載應用程序級類路徑,環境變量中指定的路徑等信息。
上面的類裝載器在加載類文件時遵循委托層次算法(Delegation Hierarchy Algorithm)。
1.2 鏈接
(1) 驗證(Verify):字節碼驗證器將驗證生成的字節碼是否正確,如果驗證失敗,將提示驗證錯誤;
(2) 準備(Prepare):對于所有靜態變量,內存將會以默認值進行分配;
(3) 解釋(Resolve):有符號存儲器引用都將替換為來自方法區(Method Area)的原始引用。
1.3 初始化
這是類加載的最后階段,所有的靜態變量都將被賦予原始值,并且靜態區塊將被執行。
2. 運行時數據區
運行時數據區可分為5個主要組件:
(1) 方法區(Method Area):所有的類級數據將存儲在這里,包括靜態變量。每個JVM只有一個方法區,它是一個共享資源;
Java虛擬機具有在所有Java虛擬機線程之間共享的方法區域。該方法區域類似于常規語言的編譯代碼的存儲區域,或者類似于操作系統過程中的“文本”段。它存儲每個類的結構,例如運行時常量池,字段和方法數據,以及方法和構造函數的代碼,包括用于類和實例初始化以及接口初始化的特殊方法。
方法區域是在虛擬機啟動時創建的。盡管方法區域在邏輯上是堆的一部分,但是簡單的實現可以選擇不進行垃圾回收或壓縮。該規范沒有規定方法區域的位置或用于管理已編譯代碼的策略。方法區域可以是固定大小的,或者可以根據計算的需要進行擴展,如果不需要更大的方法區域,則可以縮小。方法區域的內存不必是連續的。
Java虛擬機實現可以為程序員或用戶提供對方法區域初始大小的控制,以及在方法區域大小可變的情況下,可以控制最大和最小方法區域大小。
以下異常條件與方法區域相關聯:
如果無法提供方法區域中的內存來滿足分配請求,則Java虛擬機將拋出一個OutOfMemoryError。
方法區在jdk1.6/1.8版本之間的實現差異:
?
(2) 堆區域(Heap Area):所有對象及其對應的實例變量和數組將存儲在這里。每個JVM也只有一個堆區域。由于方法和堆區域共享多個線程的內存,所存儲的數據不是線程安全的;
(3) 堆棧區(Stack Area):對于每個線程,將創建單獨的運行時堆棧。對于每個方法調用,將在堆棧存儲器中產生一個條目,稱為堆棧幀。所有局部變量將在堆棧內存中創建。堆棧區域是線程安全的,因為它不共享資源。
堆棧框架分為三個子元素:
- 局部變量數組(Local Variable Array):與方法相關,涉及局部變量,并在此存儲相應的值
- 操作數堆棧(Operand stack):如果需要執行任何中間操作,操作數堆棧將充當運行時工作空間來執行操作
- 幀數據(Frame Data):對應于方法的所有符號存儲在此處。在任何異常的情況下,捕獲的區塊信息將被保持在幀數據中;
其他:
一般在使用遞歸、及循環引用容易導致棧內存溢出,因此在編程的邏輯中應避免無限制的遞歸及無終止的循環依賴等邏輯。
?
(4) PC寄存器(PC Registers):每個線程都有單獨的PC寄存器,用于保存當前執行指令的地址。一旦執行指令,PC寄存器將被下一條指令更新;
(5) 本地方法堆棧(Native Method stacks):本地方法堆棧保存本地方法信息。對于每個線程,將創建一個單獨的本地方法堆棧。
3.執行引擎
分配給運行時數據區的字節碼將由執行引擎執行,執行引擎讀取字節碼并逐個執行。
(1) 解釋器:解釋器更快地解釋字節碼,但執行緩慢。解釋器的缺點是當一個方法被調用多次時,每次都需要一個新的解釋;
(2) JIT編譯器:JIT編譯器消除了解釋器的缺點。執行引擎將在轉換字節碼時使用解釋器的幫助,但是當它發現重復的代碼時,將使用JIT編譯器,它編譯整個字節碼并將其更改為本地代碼。這個本地代碼將直接用于重復的方法調用,這提高了系統的性能。JIT的構成組件為:
- 中間代碼生成器(Intermediate Code Generator):生成中間代碼
- 代碼優化器(Code Optimizer):負責優化上面生成的中間代碼
- 目標代碼生成器(Target Code Generator):負責生成機器代碼或本地代碼
- 分析器(Profiler):一個特殊組件,負責查找熱點,即該方法是否被多次調用;
(3) 垃圾收集器(Garbage Collector):收集和刪除未引用的對象。可以通過調用“System.gc()”觸發垃圾收集,但不能保證執行。JVM的垃圾回收對象是已創建的對象。
(4) Java本機接口(JNI):JNI將與本機方法庫進行交互,并提供執行引擎所需的本機庫。
(5) 本地方法庫(Native Method Libraries):它是執行引擎所需的本機庫的集合。
總結
- 上一篇: 2022-2028年中国铅锌精矿粉行业市
- 下一篇: 2022-2028年中国汽轮发电机行业市