Jvm類加載的過程
類加載的時機
類從被加載到虛擬機內存開始,到卸載出內存為止,整個生命周期包括:加載,驗證,准備,解析,初始化,使用和卸載
;
規定5種情況:
- 遇到new,getstatic,putstatic或invokestatic這四條字節碼指令時,如果類沒有進行過初始化,則需要先觸發初始化
- 使用java.lang.reflect包的方法對類進行反射調用的時候,如果類沒有進行過初始化,則需要先觸發其初始化,
- 當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化
- 當虛擬機啟動時,用戶需要指定一個要執行的主類(包含main方法的類),虛擬機會先初始化這個類
- 當時用,JDK1.7的動態語言支持時,如果一個java.lang.invoke.MethodHandle實例后的解析結果是REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,並且這個方法句柄對應的類還沒有進行過初始化,則需要先觸發其初始化
類加載過程
加載
- 加載是類加載過程的一個階段,在加載階段虛擬機需要完成三件事
- 通過一個類的全限定名來獲取定義此類的輔而進之字節流
- 講字節流所代表的的靜態存儲結構轉化為方法區的運行時數據結構
- 在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口
驗證
- 驗證就是確保Class文件的字節流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全
- 文件格式驗證:驗證字節流是否符合Class文件格式的規范
- 元數據驗證:對字節碼描述的信息進行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的信息符合Java語言規范的要求
- 字節碼驗證:通過數據流和控制流分析,確定程序語義是合法的、符合邏輯的
- 符號引用驗證:確保解析動作能正確執行。
准備
- 准備階段是正式為類靜態變量分配內存並設置類變量初始值的階段,這些變量所使用的內存都在方法區中進行分配
- 這時候進行內存分配的僅包括類變量(static),而不包括實例變量,實例變量會在對象實例化時隨着對象一塊分配在Java堆中
- 這里所設置的初始值通常情況下是數據類型默認的零值(如0、0L、null、false等),而不是被在Java代碼中被顯式地賦予的值
public static int value = 123 //在准備階段 value的值是 0 並不是123
- 如果屬性有Constant Value 屬性,那么在准備階段變量就會被初始化為所指定的值
public static final int value = 123 // 准備階段value 的值為123
解析
- 解析階段是虛擬機將常量池內的符號引用替換為直接引用的過程
- 直接引用:直接引用可以使直接指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄
- 符號引用:符號引用以一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義的定位到目標即可
主要包含:
- 類或接口的解析
- 字段解析
- 類方法解析
- 接口方法解析
初始化
- 初始化,為類的靜態變量賦予正確的初始值,JVM負責對類進行初始化,主要對類變量進行初始化。在Java中對類變量進行初始值設定有兩種方式:
①聲明類變量是指定初始值
②使用靜態代碼塊為類變量指定初始值 - JVM初始化步驟:
1、假如這個類還沒有被加載和連接,則程序先加載並連接該類
2、假如該類的直接父類還沒有被初始化,則先初始化其直接父類
3、假如類中有初始化語句,則系統依次執行這些初始化語句 - 類的初始化,見開頭
結束生命周期
在如下幾種情況下,Java虛擬機將結束生命周期
- 執行了 System.exit()方法
- 程序正常執行結束
- 程序在執行過程中遇到了異常或錯誤而異常終止
- 由於操作系統出現錯誤而導致Java虛擬機進程終止
類加載器
把類加載階段的“通過一個類的全限定名來獲取描述此類的二進制字節流”這個動作交給虛擬機之外的類加載器來完成。這樣的好處在於,我們可以自行實現類加載器來加載其他格式的類,只要是二進制字節流就行,這就大大增強了加載器靈活性。
- 啟動類加載器
- 擴展類加載器
- 應用程序類加載器
- 雙親委派機制
如果一個類加載器收到了類加載的請求,他首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去加載,每一層次的類加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啟動類加載器中,只有當父加載器反饋無法完成這個加載請求時,自家在其才會嘗試自己加載
- 雙親委派機制