JAVA ClassLoader雙親委派機制細節研究

我們都知道,Java類都是靠ClassLoader來加載的,而類加載器也是java類,因而java類加載器本身也要被類加載器加載,顯然必須有第一個類加載器不是java類,這個加載器正是BootstrapClassLoader。由於它不是Java類,因此它不需要被別人加載,而嵌套在Java虛擬機內核裏面,也就是JVM啓動的時候Bootstrap就已經啓動,它是用C++寫的二進制代碼(不是字節碼),它可以去加載別的類。

我們還知道,JAVA虛擬機的ClassLoader採用“雙親委派”機制,如下圖所示:

圖中BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader是JVM中內置了三個重要的 ClassLoader,其中:

BootstrapClassLoader 負責加載 JVM 運行時核心類,這些類位於 JAVA_HOME/lib/rt.jar 文件中,我們常用內置庫 java.xxx.* 都在裏面,比如 java.util.*、java.io.*、java.nio.*、java.lang.* 等等。這個 ClassLoader 比較特殊,它是由 C 代碼實現的,我們將它稱之爲“根加載器”。

ExtensionClassLoader 負責加載 JVM 擴展類,比如 swing 系列、內置的 js 引擎、xml 解析器 等等,這些庫名通常以 javax 開頭,它們的 jar 包位於 JAVA_HOME/lib/ext/*.jar 中,有很多 jar 包。

AppClassLoader 纔是直接面向我們用戶的加載器,它會加載 Classpath 環境變量裏定義的路徑中的 jar 包和目錄。我們自己編寫的代碼以及使用的第三方 jar 包通常都是由它來加載的。

所謂“雙親委派”就是指當前類加載器在決定加載一個類之前,不是直接自己加載,而是先委派其父類來加載,如果其父類能加載該類,則直接返回父類加載的類,否則纔會自行加載。

此外,參考博客老大難的 Java ClassLoader,到了該徹底理解它的時候了中還有如下一段描述:

圖中的 ExtensionClassLoader 的 parent 指針畫了虛線,這是因爲它的 parent 的值是 null,當 parent 字段是 null 時就表示它的父加載器是「根加載器」。如果某個 Class 對象的 classLoader 屬性值是 null,那麼就表示這個類也是“根加載器”加載的。

看到這段話我不禁疑惑,既然ExtensionClassLoader的parent爲null,那麼ExtensionClassLoader是如何委派給BootstrapClassLoader呢?直到我看了ClassLoader.java這個類的源碼:

public abstract class ClassLoader {
    protected Class<?> loadClass(String name, boolean resolve) 
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            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 thrown if class not found
                    // from the non-null parent class loader
                }
    
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    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;
        }
    }
    
    private Class<?> findBootstrapClassOrNull(String name)
    {
        if (!checkName(name)) return null;
    
        return findBootstrapClass(name);
    }
    
    // return null if not found
    private native Class<?> findBootstrapClass(String name);
    }

 

由源碼可知,在ClassLoader的loadClass方法中,當前的ClassLoader的parent如果爲null,則會調用其自定義的findBootstrapClassOrNull方法,而後者最終調用了findBootstrapClass方法。該方法爲native方法,由該方法名可以知道,該方法就是用於查找BootstrapClassLoader加載的Class。

至於爲什麼要將ExtensionClassLoader的parent設計爲null,由前面的介紹就很容易理解了:BootstrapClassLoader不是Java類,而是直接嵌套在Java虛擬機內核裏面,因而ExtensionClassLoader無法引用BootstrapClassLoader。

最後補充一下類加載器的命名空間:每個類加載器對應一個命名空間,命名空間起到了一個類相互隔離的作用。而關於不同類加載器對應的命名空間中的類之間的可見性如下(參考):

  • 同一個命名空間內的類是相互可以見的
  • 子加載器的命名空間包含所有父加載器的命名空間。因此由子加載器加載的類能看見父加載器加載的類。
  • 由父加載器加載的類不能看見子加載器加載的類。
  • 如果兩個加載器之間沒有直接或間接的父子關係,那麼它們各自加載的類相互不可見

 

參考博客:

1、http://blog.itpub.net/31561269/viewspace-2222522/ 老大難的 Java ClassLoader,到了該徹底理解它的時候了

2、https://www.jianshu.com/p/2000f9d805ef Java類的加載和初始化

3、https://blog.csdn.net/daochuwenziyao/article/details/77689154 類加載器簡述

4、https://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 深入探討 Java 類加載器

5、https://www.cnblogs.com/lanxuezaipiao/p/4138511.html 關於java類加載雙親委派機制的思考

6、https://blog.csdn.net/weixin_43258908/article/details/89094291 Java類的加載方式、類的初始化、類的執行方式

7、https://www.jianshu.com/p/2840e87de2b7 Java類加載器

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章