JVM 之類加載機制

作者:Lebens
鏈接:https://www.jianshu.com/p/271f37bcd424

一個 Java 類從被加載到虛擬機內存到被卸載出內存爲止,生命週期一共包括如下幾個階段:

其中驗證、準備、解析這個 3 個部分統稱爲鏈接(Linking)。

image

加載、驗證、準備、初始化和卸載這 5 個階段開始執行的順序是一定的,但不意味着這幾個階段是分開執行的,這些階段通常是相互交叉混合式進行的,通常會在一個階段執行的過程中調用、激活另一個過程。

解析階段則不一定,爲了支持 java 的運行時綁定,在某些特定的情況下解析可以在初始化階段之後開始。

加載階段

這個階段主要完成如下 3 件事情

這裏獲取字節流的方式並不侷限於 zip,還包括諸如網絡中獲取、運行時生成、其他文件生成、數據庫讀取等方式。

同時相對於類加載過程的其他階段,相對於一個非數組類的加載階段是開發者可控性最強的,因爲加載階段既可以使用系統提供的加載器,也可以用戶自定義類加載器來完成類的加載。

數組類的加載情況有所不同,雖然數組類是 JVM 直接創建的,但是數組的組件,最終還是要依靠類加載器去加載,一個數組類創建主要有如下幾點:

加載階段完成後,類的二進制字節流將按照 JVM 所需的格式存儲在方法區中,同時在內存中實例化一個 java.lang.Class 的實例對象,作爲程序訪問方法區中這些類數據的外部接口。相對於 HotSpot,這個實例對象比較特殊,雖然是一個對象,但並沒有放置在堆中,而是放置在方法區中

驗證階段

這個階段主要是確保加載的 Class 文件中的字節流包含的信息符合當前虛擬機的要求,同時不存在損害 JVM 的行爲.

從整體上看,驗證階段大致上會完成下面 4 個階段的檢驗動作:文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證

文件格式驗證

這一階段主要驗證字節流是否符合 Class 文件格式的規範:

以及等等一些步驟,通過這個步驟的驗證之後,字節流纔會進入方法區中存儲,後續的 3 個階段的驗證,都是基於方法區的存儲結構進行的

元數據驗證

這一階段主要是對字節碼描述的信息進行語義分析:

等等

字節碼驗證

這一階段將對類的方法體進行校驗分析,保證類的方法在運行時不會做出危害虛擬機安全的事件。這個階段是整個驗證過程中最複雜的一個階段

等等

沒有通過字節碼驗證的方法體一定是有問題的,但是通過字節碼驗證的方法體也不能保證一定沒問題

符號引用驗證

這個驗證發生在 JVM 將符合引用轉換成直接引用的時候,在鏈接的第三階段 --- 解析階段發生

等等

驗證階段對於 JVM 的類加載機制是非常重要的,但不是必要的,因爲對程序運行期沒有影響

準備階段

這個階段會執行類的 (),主要是爲類變量(也就是被 static 修飾的)分配內存並賦值零值的階段。所謂的零值就是默認的初始值,每種數據類型各有不同的零值(需要注意的是同時被 static final 修飾的變量在此時就賦值完畢,並不會存在賦零值的操作)。類的是編譯器自動收集類變量以及靜態代碼塊後自動合併生成的。

舉個栗子:

public static int abc = 123;
public static final int ABC = 123;

上面兩句的代碼在準備階段的區別是

解析階段

類或接口的解析
字段解析
類方法解析
接口方法解析

初始化階段

編譯器自動收集實例變量初始化以及實例代碼塊後自動合併生成類的 ()

子類初始化時會先調用父類 (),用以保證子類能正常初始化。
執行子類的 ()

使用階段

這個沒啥好說的,只要代表類的 Class 對象還能被引用到,類就還在使用當中。

卸載階段

一個類何時結束生命週期,取決於代表它的 Class 對象何時結束生命週期。類的卸載,其實就是方法區中的類的回收。判斷一個類是否可以進行回收,需要同時滿足下面 3 個條件:

上面條件僅僅說明該類可以進行回收,但是並不想類的實例對象一樣,不使用了就立馬進行回收

如上圖所示,當左側所有的引用都消失時,類可以被回收。

Java 虛擬機自帶的類加載器包括根類加載器、擴展類加載器和系統類加載器。由 Java 虛擬機自帶的類加載器所加載的類,在虛擬機的生命週期中,始終不會被卸載。Java 虛擬機本身會始終引用這些類加載器,而這些類加載器則會始終引用它們所加載的類的 Class 對象,因此這些 Class 對象始終是可觸及的。由用戶自定義的類加載器加載的類是可以被卸載的。

參考書籍

本文摘錄、整理自周志明的《深入理解 Java 虛擬機》一書,如想獲得更詳細介紹可自己查閱此書。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。