類從被加載到JVM開始到卸載出內存,會通過七個階段:加載、驗證、準備、解析、初始化、使用和卸載。java
加載是類生命週期中的第一個階段,一般須要作三件事情。bootstrap
經過一個類的權限定名來獲取類的二進制字節流。數據結構
將這個字節流進行結構化處理,轉化爲方法區(永久代)的運行時數據結構。app
在堆中生成一個表明這個類的Class對象。函數
類加載器,顧名思義就是把類加載到JVM中。加載階段就是經過類加載器將類加載到JVM中。類加載器經過類的全限定名來獲取該類的二進制字節流。oop
系統提供的類加載器被分爲三種:啓動類加載器(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
// 返回該類加載器的父類加載器。全部除BootstrapClassLoader的ClassLoader都有一個parent ClassLoader getParent() // 加載名稱爲 name的類,返回的結果是 java.lang.Class類的實例。 loadClass(String name) // 查找名稱爲 name的類,返回的結果是 java.lang.Class類的實例。 findClass(String name)
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; } }
下面來看一下AppClassLoader、ExtClassLoader、BootstrapCLassLoader三者之間的關係。
經過該圖能夠得知系統類加載器(AppClassLoader)的父類加載器是標準擴展類加載器(ExtClassLoader),標準擴展類加載器的父類加載器是啓動類加載器(BootstrapClassLoader)。咱們能夠經過源碼來分析。
因爲經過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(); }
直接看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虛擬機》