在了解ClassLoader之前,我們先回顧下Java的一些相關知識。
基礎
1. Java的主要特性
- 平台無關性
- 面向對象
- GC(Java的垃圾回收機制)
- 類庫
- 語言特性
- 異常處理
2. 為什麼JVM直接将 源碼解析成機器碼去執行
- 準備工作:每次執行都需要各種檢查
- 相容性:也可以将别的語言解析成位元組碼
Compile Once,Run Anywhere如何實作
java源碼首先被編譯成位元組碼,再由不同平台的JVM進行解析,Java語言在不同的平台上運作時不需要進行重新編譯,Java虛拟機在執行位元組碼的時候,把位元組碼 轉換成具體平台上的機器指令。
3. JVM如何加載.class檔案
- Class Loader:依據特定格式,加載class檔案到記憶體
- Execution Engine:對指令 進行 解析
- Native Interface:融合不同開發語言的原生庫為 Java所用
- Runtime Data Area:JVM記憶體空間結構模型
4. 反射
Java反射機制是在運作狀态中,對于任意一個類,都能夠知道這個類的所有屬性和方法;對于任意 一個對象,都能夠調用它的任意方法和屬性;這種動态擷取資訊以及動态調用對象方法的功能稱為 Java語言的反射機制。
5. 類從編譯到執行的過程
- 編譯器将
源檔案編譯為Xxx.java
位元組碼檔案Xxx.class
- ClassLoader 将位元組碼轉換為JVM中的
對象Class<Xxx>
- JVM利用
對象執行個體化為Class<Xxx>
對象Xxx
談談ClassLoader
ClassLoader 在 Java 中有着非常重要的作用,它主要工作在 Class 裝載的加載階段,其主要作用是從系統外部獲得 Class 二進制資料流。它是 Java 的核心元件,所有的 Class 都是由 ClassLoader 進行加載的,ClassLoader 負責通過将 Class 檔案裡的二進制資料流裝載進系統,然後交給 Java 虛拟機進行連接配接、初始化等操作。
1. ClassLoader的種類
- BootStrapClassLoader:C++ 編寫,加載核心庫 java.*
- ExcClassLoader:Java 編寫,加載擴充庫 javax.*
- AppClassLoader:Java 編寫,加載程式所在目錄
- 自定義 ClassLoader:Java 編寫,定制化加載
2. 自定義 ClassLoader 的實作
關鍵函數:
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError {
return defineClass(null, b, off, len, null);
}
3. 類的加載方式
- 隐式加載:new
- 顯示加載:loadClass,forName等
4. loadClass和forName的差別
類的裝載過程
5. loadClass和forName的差別
- Class.forName得到的class是已經初始化完成的
- Classloader.loadClass得到的class是還沒有連結的
6. 談談類加載器的雙親委派機制
不同類的加載方式和加載路徑不同,為了實作分工,各自實作各自的功能,使得邏輯更加的明确,才有這麼多共存的ClassLoader,加載類會根據各自的區域各司其職,而雙親委派機制會使這些加載器互相協調,形成一個整體。
雙親委派機制的原理圖:
loadClass源碼解析:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 首先,檢測該類是否已被加載
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
// 檢查上層是否已被加載
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果未發現類,則抛出異常
}
if (c == null) {
// 如果仍未找到,則委托findClass方法去尋找
long t1 = System.nanoTime();
// 自定義的findClass方法
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
為什麼要使用雙親委派機制去加載類?
為了避免多份同樣位元組碼的加載。(通過逐層檢查可以避免多份由各自加載的相同 class檔案 )