【JVM進階之路】十三:類加載過程


通過前面的學習,我們了解了Class文件的結構,在Class文件中描述的各類信息,最終都需要加載到虛擬機中之后才能被運行和使用。

接下來,我們開始學習JVM的類加載。

一個類從被加載到虛擬機內存中開始,到從內存中卸載,整個生命周期需要經過七個階段:加載 (Loading)、驗證(Verification)、准備(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸載(Unloading),其中驗證、准備、解析三個部分統稱為連接(Linking)。

類的生命周期

《Java虛擬機規范》 嚴格規定了有且只有六種情況必須立即對類進行“初始化”:

  • 1)遇到new、getstatic、putstatic或invokestatic這四條字節碼指令時,如果類型沒有進行過初始化,則需要先觸發其初始化階段。
  • 2)使用java.lang.reflect包的方法對類型進行反射調用的時候,如果類型沒有進行過初始化,則需要先觸發其初始化。
  • 3)當初始化類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化。
  • 4)當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
  • 5)當使用JDK 7新加入的動態語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的解析結果為REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四種類型的方法句柄,並且這個方法句柄對應的類沒有進行過初始化,則需要先觸發其初始化。
  • 6)當一個接口中定義了JDK 8新加入的默認方法(被default關鍵字修飾的接口方法)時,如果有這個接口的實現類發生了初始化,那該接口要在其之前被初始化。

這六種場景中的行為稱為對一個類型進行主動引用。

接下來我們來詳細學習Java虛擬機中類加載的全過程,即加載、驗證、准備、解析和初始化。

1、加載

加載是JVM加載的起點,具體什么時候開始加載,《Java虛擬機規范》中並沒有進行強制約束,可以交給虛擬機的具體實現來自由把握。

在加載過程,JVM要做三件事情:

加載

  • 1)通過一個類的全限定名來獲取定義此類的二進制字節流。

  • 2)將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。

  • 3)在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。

加載階段結束后,Java虛擬機外部的二進制字節流就按照虛擬機所設定的格式存儲在方法區之中了,方法區中的數據存儲格式完全由虛擬機實現自行定義,《Java虛擬機規范》未規定此區域的具體數據結構。

類型數據妥善安置在方法區之后,會在Java堆內存中實例化一個java.lang.Class類的對象, 這個對象將作為程序訪問方法區中的類型數據的外部接口。

方法區在JDK不同版本的具體實現就不再詳細說了。在JDK1.8中,類型數據存儲在元空間中。

2、驗證

驗證是連接階段的第一步,這一階段的目的是確保Class文件的字節流中包含的信息符合《Java虛擬機規范》的全部約束要求。

驗證階段大致上會完成下面四個階段的檢驗動作:文件格式驗證、元數據驗證、字節碼驗證和符號引用驗證。

驗證階段

  • 文件格式驗證

第一階段要驗證字節流是否符合Class文件格式的規范,並且能被當前版本的虛擬機處理。需要驗證魔數、版本號、常量池常量類型是否支持、指向常量的索引值等等。

  • 元數據驗證

第二階段是對字節碼描述的信息進行語義分析,以保證其描述的信息符合《Java語言規范》的要求,包括類是否有父類、父類是否繼承了final修飾的類、非抽象類是否實現了父類定義的方法、類是否與父類有矛盾等等。

  • 字節碼驗證

第三階段是整個驗證過程中最復雜的一個階段,主要目的是通過數據流分析和控制流分析,確定程序語義是合法的、符合邏輯的。

  • 符號引用驗證

最后一個階段的校驗行為發生在虛擬機將符號引用轉化為直接引用的時候,這個轉化動作將在連接的第三階段——解析階段中發生。

符號引用驗證主要驗證類是否缺少或者被禁止訪問它依賴的某些外部類、方法、字段等資源。

3、准備

准備階段是給靜態變量分配內存並設置類變量初始值的階段。

在JDK 7及之前,這些變量的內存在方法區(永久代)中分配,在JDK 8及之后,靜態變量則會隨着Class對象一起存放在Java堆中。

准備

4、解析

解析階段是Java虛擬機將常量池內的符號引用替換為直接引用的過程。

  • 符號引用(Symbolic References):符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可。
  • 直接引用(Direct References):直接引用是可以直接指向目標的指針、相對偏移量或者是一個能間接定位到目標的句柄。

解析過程

5、初始化

類的初始化階段是類加載過程的最后一個步驟,在這個階段,會根據程序員通過程序編碼制定的主觀計划去初始化類變量和其他資源。

准備階段,變量被賦的是系統要求的零值,在初始化階段,賦的是代碼里編寫的值。

初始化階段

好了,基本的類加載過程已經了解完了,接下來,我們將學習負責完成加載階段的類加載器。




參考:

【1】:《深入理解Java虛擬機:JVM高級特性與最佳實踐(第3版)》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM