深入理解 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) 方法。用戶自定義類加載器 默認父加載器是應用程序加載器

雙親委派模型

什麼是雙親委派模型

如下圖這種層次關係,稱爲 雙親委派模型雙親委派模型 要求除了頂層的類加載器外,其餘的類加載器 都應有自己的父類加載器。這裏的類加載器之間的父子關係不會以繼承的關係來實現,而都是以 組合關係來複用父加載器的代碼

雙親委派模型工作原理

  1. 一個類加載器收到了類加載請求
  2. 自己先不嘗試加載,而是委派給父類加載器去加載
  3. 由於每個類加載器都有父類,因此週而復始 直到傳送到頂層的啓動類加載器
  4. 如果父類加載器反饋無法加載,則子類嘗試自己去加載
  5. 又是一個週而復始,從頂層向下一個個嘗試,直到一個加載器成功加載

雙親委派模型源碼

源碼集中在 java.lang.ClassLoaderloadClass() 中。

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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章