Java類加載源碼閱讀

  • 啓動類加載器 BootStrap ClassLoader :最頂層的類加載器,負責加載 JAVA_HOME\lib 目錄中的,或通過-Xbootclasspath參數指定路徑中的,且被虛擬機認可(按文件名識別,如rt.jar)的類。可以通 System.getProperty("sun.boot.class.path") 查看加載的路徑。

  • 擴展類加載器 Extention ClassLoader :主要加載目錄 %JRE_HOME%\lib\ext 目錄下的 jar 包和 class 文件,或通過java.ext.dirs系統變量指定路徑中的類庫。也可以通過 System.out.println(System.getProperty("java.ext.dirs")) 查看加載類文件的路徑。

  • 應用程序類加載器 Application ClassLoader :也叫做系統類加載器,可以通過 getSystemClassLoader() 獲取,負責加載用戶路徑 classpath 上的類庫。如果沒有自定義類加載器,一般這個就是默認的類加載器。

類加載層次關係

類加載器之間的這種層次關係叫做雙親委派模型。

雙親委派模型要求除了頂層的啓動類加載器(Bootstrap ClassLoader)外,其餘的類加載器都應當有自己的父類加載器。這裏的類加載器之間的父子關係一般不是以繼承關係實現的,而是用組合實現的。

  • 下面看一段源碼

public class Launcher {    private static Launcher launcher = new Launcher();    private static String bootClassPath =        System.getProperty("sun.boot.class.path");

    public static Launcher getLauncher() {        return launcher;
    }    private ClassLoader loader;

    public Launcher() {        // Create the extension class loader
        ClassLoader extcl;        try {
            extcl = ExtClassLoader.getExtClassLoader();
        } catch (IOException e) {            throw new InternalError(                "Could not create extension class loader", e);
        }        // Now create the class loader to use to launch the application
        try {
            loader = AppClassLoader.getAppClassLoader(extcl);
        } catch (IOException e) {            throw new InternalError(                "Could not create application class loader", e);
        }        Thread.currentThread().setContextClassLoader(loader);
    }    /*
     * Returns the class loader used to launch the main application.
     */
    public ClassLoader getClassLoader() {        return loader;
    }    /*
     * The class loader used for loading installed extensions.
     */
    static class ExtClassLoader extends URLClassLoader {}    /**
      * The class loader used for loading from java.class.path.
      * runs in a restricted security context.
      */
    static class AppClassLoader extends URLClassLoader {}
複製代碼

從源碼中我們看到

(1) Launcher 初始化的時候創建了 ExtClassLoader 以及 AppClassLoader ,並將 ExtClassLoader 實例傳入到 AppClassLoader 中。

(2)雖然上一段源碼中沒見到創建 BoopStrap ClassLoader ,但是程序一開始就執行了 System.getProperty("sun.boot.class.path") 。

附上 Launcher 相關文章: blog.csdn.net/jyxmust/art…

  • 類加載器中的繼承關係 AppClassLoader 的父加載器爲 ExtClassLoader , ExtClassLoader 的父加載器爲 null , BoopStrap ClassLoader 爲頂級加載器。

類加載機制-雙親委託

當JVM加載 Test.class 類的時候

  • 首先會到自定義加載器中查找,看是否已經加載過,如果已經加載過,則返回該類。

  • 如果自定義加載器沒有加載過,則詢問上一層加載器(即 AppClassLoader )是否已經加載過 Test.class 。

  • 如果沒有加載過,則詢問上一層加載器( ExtClassLoader )是否已經加載過。

  • 如果沒有加載過,則繼續詢問上一層加載( BoopStrap ClassLoader )是否已經加載過。

  • 如果 BoopStrap ClassLoader 沒有加載過,則到自己指定類加載路徑 sun.boot.class.path 下查看是否有 Test.class 字節碼,有則加載並返回加載後的類 c = findBootstrapClassOrNull(name) 。

  • 如果還是沒找到調用 c = findClass(name) 到加載器 ExtClassLoader 指定的類加載路徑 java.ext.dirs 下查找 class 文件,有則加載並返回類。

  • 依此類推,最後到自定義類加載器指定的路徑還沒有找到 Test.class 字節碼,則拋出異常 ClassNotFoundException 。

這裏注意

每個自定義的類加載器都需要重寫 findClass 方法,該方法的作用是到指定位置查找 class文件並加載到JVM中,如果找不到則拋出 ClassNotFoundException 異常。

雙親委派模型最大的好處就是讓Java類同其類加載器一起具備了一種帶優先級的層次關係。這句話可能不好理解,我們舉個例子。比如我們要加載頂層的Java類—— java.lang.Object類,無論我們用哪個類加載器去加載 Object 類,這個加載請求最終都會委託給 Bootstrap ClassLoader ,這樣就保證了所有加載器加載的 Object 類都是同一個類。

雙親委派模型的實現比較簡單,在 java.lang.ClassLoader 的 loadClass 方法中:

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;
        }
    }
複製代碼
/**
     * Finds the class with the specified <a href="#name">binary name</a>.
     * This method should be overridden by class loader implementations that
     * follow the delegation model for loading classes, and will be invoked by
     * the {@link #loadClass <tt>loadClass</tt>} method after checking the
     * parent class loader for the requested class.  The default implementation
     * throws a <tt>ClassNotFoundException</tt>.
     *
     * @param  name
     *         The <a href="#name">binary name</a> of the class
     *
     * @return  The resulting <tt>Class</tt> object
     *
     * @throws  ClassNotFoundException
     *          If the class could not be found
     *
     * @since  1.2
     */
    protected Class<?> findClass(String name) throws ClassNotFoundException {        throw new ClassNotFoundException(name);
    }
複製代碼


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