Java類載入原始碼閱讀

leyan發表於2019-02-25

JVM自帶載入器

  • 啟動類載入器 BootStrap ClassLoader:最頂層的類載入器,負責載入 JAVA_HOMElib 目錄中的,或通過-Xbootclasspath引數指定路徑中的,且被虛擬機器認可(按檔名識別,如rt.jar)的類。可以通System.getProperty("sun.boot.class.path")檢視載入的路徑。
  • 擴充套件類載入器 Extention ClassLoader:主要載入目錄%JRE_HOME%libext目錄下的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的父載入器為ExtClassLoaderExtClassLoader的父載入器為nullBoopStrap 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.ClassLoaderloadClass方法中:

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);
    }
複製程式碼

參考連結:
www.jianshu.com/p/5f79217f2…

nomico271.github.io/2017/07/07/…

www.cnblogs.com/gdpuzxs/p/7…

相關文章