本文轉載自:Java虛擬機架構?面試的時候懵了吧,作爲過來人給你們講講
JVM被分爲三個主要的子系統:類加載器子系統、運行時數據區、執行引擎
類加載器子系統
Java的動態類加載功能是由類加載器子系統處理。當它在運行時(不是編譯時)首次引用一個類時,它加載、鏈接並初始化該類文件。
加載
類由此組件加載。啓動類加載器 (BootStrap class Loader)、擴展類加載器(Extension class Loader)和應用程序類加載器(Application class Loader) 這三種類加載器幫助完成類的加載。
- 啓動類加載器:負責從啓動類路徑中加載類,無非就是rt.jar。這個加載器會被賦予最高優先級
- 擴展類加載器:負責加載ext 目錄(jre\lib)內的類
- 應用程序類加載器:負責加載應用程序級別類路徑,涉及到路徑的環境變量等
上述的類加載器會遵循委託層次算法(Delegation Hierarchy Algorithm)加載類文件。
鏈接
- 校驗:字節碼校驗器會校驗生成的字節碼是否正確,如果校驗失敗,會得到校驗錯誤
- 準備:分配內存並初始化默認值給所有的靜態變量
- 解析:所有符號內存引用被方法區(Method Area)的原始引用所替代
初始化
這是類加載的最後階段,這裏所有的靜態變量會被賦初始值, 並且靜態塊將被執行。
運行時數據區(Runtime Data Area)
運行時數據區域被劃分爲5個主要組件:
方法區(Method Area)
所有類級別數據將被存儲在這裏,包括靜態變量。每個JVM只有一個方法區,它是一個共享的資源。
堆區(Heap Area)
所有的對象和它們相應的實例變量以及數組將被存儲在這裏。每個JVM同樣只有一個堆區。由於方法區和堆區的內存由多個線程共享,所以存儲的數據不是線程安全的。
棧區(Stack Area)
對每個線程會單獨創建一個運行時棧。對每個函數呼叫會在棧內存生成一個棧幀(Stack Frame)。所有的局部變量將在棧內存中創建。棧區是線程安全的,因爲它不是一個共享資源。棧幀被分爲三個子實體:
- 局部變量數組:包含多少個與方法相關的局部變量並且相應的值將被存儲在這裏
- 操作數棧:如果需要執行任何中間操作,操作數棧作爲運行時工作區去執行指令
- 幀數據:方法的所有符號都保存在這裏。在任意異常的情況下,catch塊的信息將會被保存在幀數據裏面
PC寄存器
每個線程都有一個單獨的PC寄存器來保存當前執行指令的地址,一旦該指令被執行,pc寄存器會被更新至下條指令的地址。
本地方法棧
本地方法棧保存本地方法信息。對每一個線程,將創建一個單獨的本地方法棧。
執行引擎
分配給運行時數據區的字節碼將由執行引擎執行。執行引擎讀取字節碼並逐段執行。
解釋器
解釋器能快速的解釋字節碼,但執行卻很慢。 解釋器的缺點就是,當一個方法被調用多次,每次都需要重新解釋。
編譯器
JIT編譯器消除了解釋器的缺點。執行引擎利用解釋器轉換字節碼,但如果是重複的代碼則使用JIT編譯器將全部字節碼編譯成本機代碼。本機代碼將直接用於重複的方法調用,這提高了系統的性能。
- 中間代碼生成器:生成中間代碼
- 代碼優化器:負責優化上面生成的中間代碼
- 目標代碼生成器:負責生成機器代碼或本機代碼
- 探測器(Profiler):一個特殊的組件,負責尋找被多次調用的方法。
垃圾回收器
收集並刪除未引用的對象。可以通過調用"System.gc()"來觸發垃圾回收,但並不保證會確實進行垃圾回收。JVM的垃圾回收只收集哪些由new關鍵字創建的對象。所以,如果不是用new創建的對象,你可以使用finalize函數來執行清理。
Java本地接口 (JNI):JNI 會與本地方法庫進行交互並提供執行引擎所需的本地庫。本地方法庫:是一個執行引擎所需的本地庫的集合。