深入理解 Java 虛擬機:雙親委派模型
類加載器種類
啓動類加載器(Bootstrap ClassLoader)
使用 C++ 實現,是虛擬機的一部分。所有其他的類加載器,都是 Java 實現,獨立於虛擬機外部,並且全程繼承自抽象類 java.lang.ClassLoader
。
啓動類加載器
負責把存放在 <JAVA_HOME>\lib 目錄中,或者被 -Xbootclasspath 參數所指定的,並且是 虛擬機識別
的類庫加載到虛擬機內存中。
擴展類加載器(Extension ClassLoader)
由 sun.misc.Launcher$ExtClassLoader
實現,負責加載 <JAVA_HOME>\lib\ext 目錄中,或者被 java.ext.dirs 系統變量所指定的路徑中的所有類庫, 開發者可以直接獲取此加載器。
應用程序類加載器(Application ClassLoader)
由 sun.misc.Launcher$AppClassLoader
實現,由於這個類加載器是 ClassLoader 中的 getSystemClassLoader() 方法的返回值,一般也稱他爲 系統類加載器。
它負責加載 用戶類路徑上所指定的類庫,開發者可以直接使用這個類加載器,如果應用程序沒有自定義過自己的類加載器,一般情況下就是 程序中默認的類加載器
。
用戶自定義類加載器(Customized Class Loader)
用戶可以自己定義類加載器來加載類。所有的類加載器都要繼承 java.lang.ClassLoader 類並重寫 findClass(String name) 方法
。用戶自定義類加載器 默認父加載器是應用程序加載器。
雙親委派模型
什麼是雙親委派模型
如下圖這種層次關係,稱爲 雙親委派模型。雙親委派模型 要求除了頂層的類加載器外
,其餘的類加載器 都應有自己的父類加載器
。這裏的類加載器之間的父子關係不會以繼承的關係來實現,而都是以 組合關係來複用父加載器的代碼
。
雙親委派模型工作原理
- 一個類加載器收到了類加載請求
- 自己先不嘗試加載,而是委派給父類加載器去加載
- 由於每個類加載器都有父類,因此週而復始
直到傳送到頂層的啓動類加載器
- 如果父類加載器反饋無法加載,則子類嘗試自己去加載
- 又是一個週而復始,
從頂層向下一個個嘗試,直到一個加載器成功加載
雙親委派模型源碼
源碼集中在 java.lang.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) {
// 父類加載器加載失敗,拋出 ClassNotFoundException 異常
// 此時的 c == null,後續開始使用自己的加載器加載
}
if (c == null) {
// c == null,意思就是被 catch 了,也就是父類加載器加載失敗
// 使用自身的加載器進行加載
// 還失敗,就會像上拋出 ClassNotFoundException
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}