1. 程式人生 > >JAVA類載入器及雙親委派模型

JAVA類載入器及雙親委派模型

一、類載入器

java中類載入器可以大致劃分為以下三類:

啟動類載入器:Bootstrap ClassLoader,負責載入存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath引數指定的路徑中的,並且能被虛擬機器識別的類庫(如rt.jar,所有的java.*開頭的類均被Bootstrap ClassLoader載入)。啟動類載入器是無法被Java程式直接引用的。

擴充套件類載入器:Extension ClassLoader,該載入器由sun.misc.Launcher$ExtClassLoader實現,它負責載入DK\jre\lib\ext目錄中,或者由java.ext.dirs系統變數指定的路徑中的所有類庫(如javax.*開頭的類),開發者可以直接使用擴充套件類載入器。

應用程式類載入器:Application ClassLoader,該類載入器由sun.misc.Launcher$AppClassLoader來實現,它負責載入使用者類路徑(ClassPath)所指定的類,開發者可以直接使用該類載入器,如果應用程式中沒有自定義過自己的類載入器,一般情況下這個就是程式中預設的類載入器。

應用程式都是由這三種類載入器互相配合進行載入的,如果有必要,我們還可以加入自定義的類載入器。因為JVM自帶的ClassLoader只是懂得從本地檔案系統載入標準的java class檔案,因此如果編寫了自己的ClassLoader,便可以做到如下幾點:

1)在執行非置信程式碼之前,自動驗證數字簽名。

2)動態地建立符合使用者特定需要的定製化構建類。

3)從特定的場所取得java class,例如資料庫中和網路中。

類載入有三種方式:

1、命令列啟動應用時候由JVM初始化載入

2、通過Class.forName()方法動態載入

3、通過ClassLoader.loadClass()方法動態載入

Class.forName()和ClassLoader.loadClass()區別:

Class.forName():將類的.class檔案載入到jvm中之外,還會對類進行解釋,執行類中的static塊;

ClassLoader.loadClass():只幹一件事情,就是將.class檔案載入到jvm中,不會執行static中的內容,只有在newInstance才會去執行static塊。

 

ClassLoader原始碼分析:

public Class<?> loadClass(String name)throws ClassNotFoundException {
            return loadClass(name, false);
    }
 
    protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {
            // 首先判斷該型別是否已經被載入
            Class c = findLoadedClass(name);
            if (c == null) {
                //如果沒有被載入,就委託給父類載入或者委派給啟動類載入器載入
                try {
                    if (parent != null) {
                         //如果存在父類載入器,就委派給父類載入器載入
                        c = parent.loadClass(name, false);
                    } else {
                    //如果不存在父類載入器,就檢查是否是由啟動類載入器載入的類,通過呼叫本地方法native Class findBootstrapClass(String name)
                        c = findBootstrapClass0(name);
                    }
                } catch (ClassNotFoundException e) {
                 // 如果父類載入器和啟動類載入器都不能完成載入任務,才呼叫自身的載入功能
                    c = findClass(name);
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }

自定義類載入器一般都是繼承自 ClassLoader 類,從上面對 loadClass 方法來分析來看,我們只需要重寫 findClass 方法即可。

 

二、雙親委派

雙親委派模型指,某個特定的類載入器在接到載入類的請求時,首先將載入任務委託給父類載入器,依次遞迴,如果父類載入器可以完成類載入任務,就成功返回;只有父類載入器無法完成此載入任務時,才自己去載入。

雙親委派模型圖解

雙親委派的意義所在:

防止記憶體中出現多份同樣的位元組碼 

比如兩個類A和類B都要載入System類:
如果不用委託而是自己載入自己的,那麼類A就會載入一份System位元組碼,然後類B又會載入一份System位元組碼,這樣記憶體中就出現了兩份System位元組碼。
如果使用委託機制,會遞迴的向父類查詢,也就是首選用Bootstrap嘗試載入,如果找不到再向下。這裡的System就能在Bootstrap中找到然後載入,如果此時類B也要載入System,也從Bootstrap開始,此時Bootstrap發現已經載入過了System那麼直接返回記憶體中的System即可而不需要重新載入,這樣記憶體中就只有一份System的位元組碼了。

雙親委派的程式碼實現原理:

雙親委派模型的程式碼實現集中在java.lang.ClassLoader的loadClass()方法當中。 
1)首先檢查類是否被載入,沒有則呼叫父類載入器的loadClass()方法; 
2)若父類載入器為空,則預設使用啟動類載入器作為父載入器; 
3)若父類載入失敗,丟擲ClassNotFoundException 異常後,再呼叫自己的findClass() 方法。

protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    //1 首先檢查類是否被載入
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
             //2 沒有則呼叫父類載入器的loadClass()方法;
                c = parent.loadClass(name, false);
            } else {
            //3 若父類載入器為空,則預設使用啟動類載入器作為父載入器;
                c = findBootstrapClass0(name);
            }
        } catch (ClassNotFoundException e) {
           //4 若父類載入失敗,丟擲ClassNotFoundException 異常後
            c = findClass(name);
        }
    }
    if (resolve) {
        //5 再呼叫自己的findClass() 方法。
        resolveClass(c);
    }
    return c;
}