類加載器

1.類加載

類從被加載到JVM開始到卸載出內存,會通過七個階段:加載、驗證、準備、解析、初始化、使用和卸載。java

2.加載

加載是類生命週期中的第一個階段,一般須要作三件事情。bootstrap

  • 經過一個類的權限定名來獲取類的二進制字節流。數據結構

  • 將這個字節流進行結構化處理,轉化爲方法區(永久代)的運行時數據結構。app

  • 在堆中生成一個表明這個類的Class對象。函數

3.類加載器

      類加載器,顧名思義就是把類加載到JVM中。加載階段就是經過類加載器將類加載到JVM中。類加載器經過類的全限定名來獲取該類的二進制字節流。oop

3.1分類

      系統提供的類加載器被分爲三種:啓動類加載器(Bootstrap ClassLoader)、擴展類加載器(Extension ClassLoader)、應用程序類加載器(Application ClassLoader)this

  • 啓動類加載器(Bootstramp ClassLoader):JVM的一部分,在Hot Spot是由C++中實現的。它負責將<JAVA_HOME>/lib目錄下面的類庫進行加載,而且加載的類庫必須是JVM所識別的,例如rt.jar。若是你本身實現封裝一個jar包丟到<JAVA_HOME>/lib目錄下面,是永遠也加載不到的,由於啓動類加載器不會去加載它不識別的類庫。url

  • 擴展類加載器(Extension ClassLoader):獨立於JVM,實現類爲:ExtClassLoader,是由Java實現的類加載器,繼承於抽象類ClassLoader。它負責加載<JAVA_HOME>/lib/ext目錄下的類庫。spa

  • 應用程序類加載器(Application ClassLoader):獨立於JVM,實現類爲:AppClassLoader,是由Java實現的類加載器,繼承於抽象類ClassLoader。咱們能夠經過調用ClassLoader.getSystemClassLoader()方法會獲取AppClassLoader的實例。它負責加載classpath上制定的類庫,咱們全部自定義的通常都是經過AppClassLoader來進行加載。code

3.2方法 

// 返回該類加載器的父類加載器。全部除BootstrapClassLoader的ClassLoader都有一個parent ClassLoader
getParent()

// 加載名稱爲 name的類,返回的結果是 java.lang.Class類的實例。
loadClass(String name)

// 查找名稱爲 name的類,返回的結果是 java.lang.Class類的實例。
findClass(String name)


3.3雙親委派

      JVM在加載類時默認採用的是雙親委派機制。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委託給父類加載器,依次遞歸,若是父類加載器能夠完成類加載任務,就成功返回;只有父類加載器沒法完成此加載任務時,才本身去加載。關於虛擬機默認的雙親委派機制,咱們能夠從系統類加載器和標準擴展類加載器爲例做簡單分析。  

      首先看下AppClassLoader、ExtClassLoader的繼承體系

    

    

      而後咱們來看AppClassLoader、ExtClassLoader、UrlClassLoader、ClassLoader的loadClass方法,來看看它們是怎樣load Class到JVM的。         

  • AppClassLoader

public Class loadClass(String var1, boolean var2) throws ClassNotFoundException {
    int var3 = var1.lastIndexOf(46);
    if(var3 != -1) {
        SecurityManager var4 = System.getSecurityManager();
        if(var4 != null) {
            var4.checkPackageAccess(var1.substring(0, var3));
        }
    }
	// 調用父類的loadClass()
    return super.loadClass(var1, var2);
}
  • ExtClassLoader :

  • UrlClassLoader

public final Class loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    // First check if we have permission to access the package. This
    // should go away once we've added support for exported packages.
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        int i = name.lastIndexOf('.');
        if (i != -1) {
            sm.checkPackageAccess(name.substring(0, i));
        }
    }
	// 調用父類的loadClass()
    return super.loadClass(name, resolve);
}

能夠從這三個類來分析。

首先ExtClassLoader沒有重寫ClassLoader的loadClass()方法,因此當調用ExtClassLoader.loadClass()時,會調用ClassLoader.loadClass()

AppClassLoader、UrlClassLoader都重寫了loadClass()方法,但最終仍是經過super.loadClass()方法來實現類的加載。

因此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);
		// 若是class沒有被加載過,則進行加載
        if (c == null) {
            long t0 = System.nanoTime();
            try {
				// 若是父加載器不爲null,則調用父加載器的loadClass方法
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
					// 若是父加載器爲null,則拿到bootstrap加載器,該加載器不是用java來實現的
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }
            if (c == null) {
				// 若是父加載器加載失敗,那麼就本身來加載。
				// findClass是個抽象方法,真正的實如今每一個ClassLoader中本身實現。
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}


3.3.1 雙親委派關係創建

下面來看一下AppClassLoader、ExtClassLoader、BootstrapCLassLoader三者之間的關係。

經過該圖能夠得知系統類加載器(AppClassLoader)的父類加載器是標準擴展類加載器(ExtClassLoader),標準擴展類加載器的父類加載器是啓動類加載器(BootstrapClassLoader)。咱們能夠經過源碼來分析。

3.3.1.1 AppClassLoader與ExtClassLoader關係創建

因爲經過ClassLoader.getSystemClassLoader()來獲取AppClassLoader,咱們能夠從這裏入手。

public static ClassLoader getSystemClassLoader() {
	// 初始化SystemClassLoader,即AppClassLoader
    initSystemClassLoader();
    if (scl == null) {
        return null;
    }
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkClassLoaderPermission(scl, Reflection.getCallerClass());
    }
    return scl;
}

接下來看如何初始化SystemClassLoader

private static synchronized void initSystemClassLoader() {
	// scl是否被初始化,若是沒有,則進行初始化
    if (!sclSet) {
        if (scl != null)
            throw new IllegalStateException("recursive invocation");
		// 初始化SystemClassLoader的關鍵
        sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
        if (l != null) {
            Throwable oops = null;
            scl = l.getClassLoader();
        }
        sclSet = true;
    }
}

獲取Launcher

public class Launcher {
    private static Launcher launcher = new Launcher();
    private ClassLoader loader;
    public static Launcher getLauncher() {
        return launcher;
    }
    public Launcher() {
        Launcher.ExtClassLoader var1;
        try {
			// 獲取ExtClassLoader
            var1 = Launcher.ExtClassLoader.getExtClassLoader();
        } catch (IOException var10) {
            throw new InternalError("Could not create extension class loader");
        }
        try {
			// 獲取AppClassLoader,即SystemClassLoader,將var1(ExtClassLoader)當作參數
            this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
        } catch (IOException var9) {
            throw new InternalError("Could not create application class loader");
        }
    }
    public ClassLoader getClassLoader() {
        return this.loader;
    }
}
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    final String var1 = System.getProperty("java.class.path");
    final File[] var2 = var1 == null?new File[0]:Launcher.getClassPath(var1);
    return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction() {
        public Launcher.AppClassLoader run() {
            URL[] var1x = var1 == null?new URL[0]:Launcher.pathToURLs(var2);
			// var0即剛纔傳進來的ExtClassLoader
            return new Launcher.AppClassLoader(var1x, var0);
        }
    });
}
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
    final String var1 = System.getProperty("java.class.path");
    final File[] var2 = var1 == null?new File[0]:Launcher.getClassPath(var1);
    return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction() {
        public Launcher.AppClassLoader run() {
            URL[] var1x = var1 == null?new URL[0]:Launcher.pathToURLs(var2);
			// var0即剛纔傳進來的ExtClassLoader
            return new Launcher.AppClassLoader(var1x, var0);
        }
    });
}
AppClassLoader(URL[] var1, ClassLoader var2) {
	// AppClassLoader繼承於URLClassLoader,調用父構造函數,var2即ExtClassLoader
    super(var1, var2, Launcher.factory);
}
public URLClassLoader(URL[] urls, ClassLoader parent,
                      URLStreamHandlerFactory factory) {
	// parent即ExtClassLoader
    super(parent);
    // this is to make the stack depth consistent with 1.1
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkCreateClassLoader();
    }
    ucp = new URLClassPath(urls, factory);
    acc = AccessController.getContext();
}


3.3.1.2 ExtClassLoader與BootstrapClassLoader關係創建

    直接看ExtClassLoader的構造函數

public ExtClassLoader(File[] var1) throws IOException {
	// parent爲null
    super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
}

    也就是說ExtClassLoader的parent爲null,那ExtClassLoader與BootstrapClassLoader的關係是如何創建的?下面回顧一下雙親委派的實現。

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 {
					// 若是父加載器爲null,則拿到bootstrap加載器。
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
            }
            if (c == null) {
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

    ExtClassLoader的parent爲null,當parent爲null時,會直接用BootstrapClassLoader來當作父加載器。由於BootstrapClassLoader不是用JAVA實現的,因此ExtClassLoader沒法引用。


參考文獻:《深刻JVM虛擬機》

相關文章
相關標籤/搜索