JVM 之 (14) 類加載器詳解和雙親委派模型

類加載器

        虛擬機設計團隊把類加載階段中「經過一個類的全限定名來獲取描述此類的二進制字節流」這個動做放到Java虛擬機外部去實現,以便讓應用程序本身決定如何去獲取所須要的類。實現這個動做的模塊稱爲「類加載器」。

類加載器分類  

    啓動(Bootstrap)類加載器
        啓動類加載器主要加載的是JVM自身須要的類,這個類加載使用C++語言實現的,是虛擬機自身的一部分,它負責將 <JAVA_HOME>/lib路徑下的核心類庫或-Xbootclasspath參數指定的路徑下的jar包加載到內存中,注意必因爲虛擬機是按照文件名識別加載jar包的,如rt.jar,若是文件名不被虛擬機識別,即便把jar包丟到lib目錄下也是沒有做用的(出於安全考慮,Bootstrap啓動類加載器只加載包名爲java、javax、sun等開頭的類)。

    擴展(Extension)類加載器
        擴展類加載器是指Sun公司(已被Oracle收購)實現的sun.misc.Launcher$ExtClassLoader類,由Java語言實現的,是Launcher的靜態內部類,它負責加載<JAVA_HOME>/lib/ext目錄下或者由系統變量-Djava.ext.dir指定位路徑中的類庫,開發者能夠直接使用標準擴展類加載器。
//ExtClassLoader類中獲取路徑的代碼
private static File[] getExtDirs() {
     //加載<JAVA_HOME>/lib/ext目錄中的類庫
     String s = System.getProperty("java.ext.dirs");
     File[] dirs;
     if (s != null) {
         StringTokenizer st =
             new StringTokenizer(s, File.pathSeparator);
         int count = st.countTokens();
         dirs = new File[count];
         for (int i = 0; i < count; i++) {
             dirs[i] = new File(st.nextToken());
         }
     } else {
         dirs = new File[0];
     }
     return dirs;
 }

系統(System)類加載器/應用程序類加載器
        指 Sun公司實現的sun.misc.Launcher$AppClassLoader。它負責加載系統類路徑java -classpath或-D java.class.path 指定路徑下的類庫,也就是咱們常常用到的classpath路徑,開發者能夠直接使用系統類加載器,通常狀況下該類加載是程序中默認的類加載器,經過ClassLoader#getSystemClassLoader()方法能夠獲取到該類加載器。

自定義類加載器
java

//該加載器能夠加載與本身在同一路徑下的Class文件  
public class MyClassLoader extends ClassLoader{  
    @Override  
    public Class<?> loadClass(String name) throws ClassNotFoundException{  
        try {    
            String fileName=name.substring(name.lastIndexOf(".")+1)+".class";  
            InputStream is=getClass().getResourceAsStream(fileName);    
            if(is==null){  
                //不在當前路徑下的類,例如Object類(JavaBean的父類),採用委派模型加載  
                return super.loadClass(name);   
            }else{  
                //在當前路徑下的類,例如JavaBean類,直接經過本身加載其Class文件  
                byte[] b=new byte[is.available()];  
                is.read(b);  
                return defineClass(name,b,0,b.length);     
            }  
  
        } catch (IOException e) {   
            throw new ClassNotFoundException();  
        }  
    }  
}  

類的惟一性

    對於任意一個類,都須要由加載它的類加載器類的全限定名一同肯定其在Java虛擬機中的惟一性
    只有被同一個類加載器加載的類纔可能會想等。相同的字節碼被不一樣的類加載器加載的類不想等。
bootstrap

public class ClassLoaderTest {

    public static void main(String[] args) throws Exception{
        ClassLoader myLoader=new MyClassLoader();
        Object classLoaderTest=myLoader.loadClass("com.loader.ClassLoaderTest").newInstance();
        System.out.println(classLoaderTest.getClass());
        System.out.println(classLoaderTest instanceof  ClassLoaderTest);
    }
}
class com.loader.ClassLoaderTest
false

雙親委派模型

        類加載器之間的層次關係,稱爲類加載器的雙親委派模型。雙親委派模型要求除了頂層的啓動類加載器外,其他類加載器都應該有本身的父類加載器。注意,這裏類加載器之間的父子關係通常不會以繼承的關係實現,而是使用組合關係來複用父加載器的代碼。
    
安全


雙親委派工做原理

        若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每個層次的類加載器都是如此,所以全部的加載請求最終都應該首先傳送到頂層的啓動類加載器中,只有當父加載器反饋本身沒法完成這個加載請求時,子加載器纔會嘗試本身去加載。 若是最初發起的類加載的類加載器也沒法完成加載請求時候,將會拋出ClassNotFound,而再也不調用子類的加載器去加載請求。
     

雙親委派源碼
  類加載器均是繼承自java.lang.ClassLoader抽象類。首先,咱們看一看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 {  
                        //父加載器爲null,說明this爲擴展類加載器的實例,父加載器爲啓動類加載器  
                        c = findBootstrapClassOrNull(name);  
                    }  
                } catch (ClassNotFoundException e) {  
                    // 若是父加載器拋出ClassNotFoundException  
                    // 說明父加載器沒法完成加載請求  
                    // ClassNotFoundException thrown if class not found  
                    // from the non-null parent class loader  
                }  
  
                if (c == null) {  
                    // 若是父加載器沒法加載  
                    // 調用自己的findClass方法來進行類加載  
                    // 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;  
        }  
    }  
protected Class<?> loadClass(String name, boolean resolve)  
    throws ClassNotFoundException  
{  
..  
                if (parent != null) {//父加載器不爲null,即父加載器爲ClassLoader類型  
                    c = parent.loadClass(name, false);//委派請求給父加載器  
                } else {//父加載器爲null,說明this爲擴展類加載器的實例          
                    c = findBootstrapClassOrNull(name);//經過啓動類加載器加載類  
                }  
..  
}  
/**經過啓動類加載器加載類 
 * Returns a class loaded by the bootstrap class loader; 
 * or return null if not found. 
 */  
private Class findBootstrapClassOrNull(String name)  
{  
    if (!checkName(name)) return null;  
  
    return findBootstrapClass(name);  
}  
// return null if not found 啓動類加載器經過本地方法加載類  
private native Class findBootstrapClass(String name);  
默認狀況下,應用程序中的類由 應用程序類加載器 AppClassLoader )加載。該加載器加載 系統類路徑 下的類,所以通常也稱爲 系統類加載器

雙親委派優點

    類加載器一塊兒具有了一種帶優先級的層次關係,越是基礎的類,越被上層的類加載器鎖加載,保證了java程序的穩定性。
相關文章
相關標籤/搜索