目錄
類的生命週期
類加載的過程
階段1:加載
加載是類加載的第一個階段,需要完成三件事情:
- 通過一個類的全限定名來獲取定義該類的二進制字節流。
- 將這個字節流的表現的“靜態存儲結構”轉化爲方法區的運行時數據結構。
- 在內存中生成一個Class對象,作爲這個類的類對象,也是這個類的各種數據的訪問入口。
可以理解爲加載階段的任務就是將Class文件中的類 加載進方法區中。
階段2:驗證
驗證是爲了確保加載進來的類信息符合虛擬機的要求。
驗證分爲四種:
- 文件格式驗證
- 元數據驗證
- 字節碼驗證
- 符號引用驗證
由於不是特別重要,就不展開了,有興趣瞭解可以百度。
階段3:準備
準備階段是正式爲類變量分配內存並初始化的階段,這裏的類變量僅僅是類中定義的static變量!非static變量是在實例對象的時候隨對象一起分配在堆中!
所有的static變量將會被初始化爲零值。
public static int a=123;
上述代碼中,在準備階段,a會被初始化爲0,然後在接下來的類初始化階段纔會賦值爲123。
階段4:解析
解析階段是將常量池中的符號引用替換爲直接引用的過程。不同虛擬機實現可以根據需要判斷到底是在類被加載器加載的時候對常量池的符號引用進行解析(也就是初始化之前),還是等到一個符號引用被使用之前進行解析(也就是在初始化之後)。
如果一個符號引用進行多次解析請求,虛擬機可以對第一次解析的結果進行緩存(在運行時常量池中記錄引用,並把常量標識爲一解析狀態),這樣就避免了一個符號引用的多次解析。但是invokedynamic指令除外,因爲invokedynamic指令執行時,之前緩存的解析結果不一定有效,所以調用invokedynamic指令時需要重新解析。
解析動作主要針對的是類或者接口、字段、類方法、方法類型、方法句柄和調用點限定符7類符號引用。
階段5:初始化
初始化階段是類加載過程的最後一步,這一步才真正開始執行類中定義的Java程序代碼。
初始化階段是執行類構造器<clinit()>方法的過程。
<clinit()>方法是由編譯器自動按順序收集類變量賦值操作和靜態初始化塊(static{})中的語句合併產生的。
關於靜態初始化塊的介紹請看我的另一篇博文
<clinit()>方法的幾個注意事項
- <clinit()>方法和類的構造函數不一樣,不需要顯式調用父類構造器,虛擬機會保證在<clinit()>方法執行前其父類的<clinit()>方法已經執行完畢。
- <clinit()>方法不是必須的,如果一個類中沒有類變量的賦值操作和靜態初始化塊,則虛擬機不會爲其生成<clinit()>方法。
- 接口或者類的<clinit()>方法不需要先執行父接口的<clinit()>方法,只有當父接口中的變量或者方法被使用時,纔會執行父接口的<clinit()>方法。
- <clinit()>方法是同步的。