JVM核心知識-類載入機制
阿新 • • 發佈:2019-12-31
JVM中類的生命週期包括7個階段,載入、準備、驗證、解析、初始化、使用、解除安裝。其中準備、驗證、解析被歸為連線階段。
載入
jvm在這個階段完成的工作
- 通過類名獲取類的二進位制位元組流
- 將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構
- 在堆中生成一個代表該類的
java.lang.class
物件,作為訪問類在方法區中資料的入口
在這個階段開發者可以控制二進位制位元組流的獲取,也就是可以通過自定義的類載入器做自己定製化的操作。
驗證
顧名思義,驗證被載入的類的正確性。
- 檔案格式驗證:驗證位元組流是否符合Class檔案格式的規範;例如:是否以 0xCAFEBABE開頭
- 元資料驗證:對位元組碼描述的資訊進行語義分析,以保證其描述的資訊符合Java語言規範的要求
- 位元組碼驗證:通過資料流和控制流分析,確定程式語義是合法的、符合邏輯的。
- 符號引用驗證:確保解析動作能正確執行
準備
為類的 靜態變數分配記憶體,並將其初始化為預設值
- 只分配靜態變數記憶體
- 初始化預設值是型別的預設值(即int:0、boolean:false...),不是程式碼顯示設定的初始值
- 如果是
final static
修飾的變數則會賦值為程式碼中的初始值(即:final static int val=3,這時val賦值為3,而不是0)
解析
把類中的符號引用轉換為直接引用符號引用就是一組符號來描述目標(例如:ArrayList)。直接引用就是直接指向目標的指標、相對偏移量或一個間接定位到目標的控制程式碼。
初始化
類變數的初始化
- 定義類變數時初始化
- 靜態程式碼塊初始化
觸發類初始化的場景
- 建立類例項,即new物件
- 訪問靜態變數
- 訪問靜態方法
- 反射呼叫(即Class.forName("com.xxx.Obj"),Obj類被初始化)
- 子類被初始化則父類被初始化
類載入器
- 啟動類載入器(BootStrap ClassLoader):負責載入jrelib下或者-Xbootclasspath 引數指定的路徑下的能被jvm識別的類庫。開發者無法直接使用
- 拓展類載入器(Extension ClassLoader):
sun.misc.Launcher$ExtClassLoader
,它負責載入 jrelibext目錄中,或者由 java.ext.dirs系統變數指定的路徑中的所有類庫。開發者可以直接使用。 - 應用類載入器(Application ClassLoader):
sun.misc.Launcher$AppClassLoader
,它負責載入使用者類路徑(ClassPath)所指定的類。開發者可以直接使用 - 自定義類載入器(Custom ClassLoader):使用者可以自定義類載入器
雙親委派模型
雙親委派模型的實現,當一個類載入器需要載入類時,會把這個任務委派給父級類載入器,依次向上,醉倒頂層啟動類載入器為止,如果父級無法載入,再自己處理載入。雙親委派模型的好處是,保證同一類環境中只有一個相同的類。也就是說JVM中判斷是否是同一個類的條件是,是否相同的類載入器,類本身相同。程式碼示例:
public class ClassLoaderTest {
public static void main(String[] args) throws ClassNotFoundException,IllegalAccessException,InstantiationException {
// 使用ClassLoaderTest的類載入器載入本類
Object obj1 = ClassLoaderTest.class.getClassLoader().loadClass("com.ognice.ClassLoaderTest").newInstance();
System.out.println(obj1.getClass().getClassLoader());
System.out.println(obj1 instanceof ClassLoaderTest);
// 使用自定義類載入器載入本類
ClassLoader customClassLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
System.out.println("custom classloader loading " + name);
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream stream = getClass().getResourceAsStream(fileName);
if (stream == null) {
return super.loadClass(name);
}
try {
byte[] b = new byte[stream.available()];
stream.read(b);
return defineClass(name,b,b.length);
} catch (IOException e) {
e.printStackTrace();
}
// 父級找class
return super.loadClass(name);
}
};
Object obj2 = customClassLoader.loadClass("com.ognice.ClassLoaderTest").newInstance();
System.out.println(obj2.getClass().getClassLoader());
System.out.println(obj2 instanceof ClassLoaderTest);
}
}複製程式碼
執行結果
sun.misc.Launcher$AppClassLoader@18b4aac2
true
custom classloader loading com.ognice.ClassLoaderTest
custom classloader loading java.lang.Object
custom classloader loading java.lang.ClassLoader
custom classloader loading com.ognice.ClassLoaderTest$1
com.ognice.ClassLoaderTest$1@277c0f21
false複製程式碼
本文由部落格一文多發平臺 OpenWrite 釋出!