Java --ClassLoader建立、加載class、卸載class

1、java提供了三種ClassLoader對Class進行加載:java

1.BootStrap ClassLoader:稱爲啓動類加載器,是Java類加載層次中最頂層的類加載器,負責加載JDK中的核心類庫,如:rt.jar、resources.jar、charsets.jar等,可經過以下程序得到該類加載器從哪些地方加載了相關的jar或class文件:算法

URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();  
for (int i = 0; i < urls.length; i++) {  
    System.out.println(urls[i].toExternalForm());  
}  

或者api

System.out.println(System.getProperty("sun.boot.class.path")); 

最後結果爲:數組

/Java/jdk1.6.0_22/jre/lib/resources.jar
/Java/jdk1.6.0_22/jre/lib/rt.jar
/Java/jdk1.6.0_22/jre/lib/sunrsasign.jar
/Java/jdk1.6.0_22/jre/lib/jsse.jar
/Java/jdk1.6.0_22/jre/lib/jce.jar
/Java/jdk1.6.0_22/jre/lib/charsets.jar
/Java/jdk1.6.0_22/jre/classes/緩存

 

2.Extension ClassLoader:稱爲擴展類加載器,負責加載Java的擴展類庫,默認加載JAVA_HOME/jre/lib/ext/目下的全部jar。安全

3.App ClassLoader:稱爲系統類加載器,負責加載應用程序classpath目錄下的全部jar和class文件。網絡

 

2、ClassLoader的加載原理架構

 一、原理介紹

       ClassLoader使用的是雙親委託模型來搜索類的,每一個ClassLoader實例都有一個父類加載器的引用(不是繼承的關係,是一個包含的關係),虛擬機內置的類加載器(Bootstrap ClassLoader)自己沒有父類加載器,但能夠用做其它ClassLoader實例的的父類加載器。當一個ClassLoader實例須要加載某個類時,它會試圖親自搜索某個類以前,先把這個任務委託給它的父類加載器,這個過程是由上至下依次檢查的,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,若是沒加載到,則把任務轉交給Extension ClassLoader試圖加載,若是也沒加載到,則轉交給App ClassLoader 進行加載,若是它也沒有加載獲得的話,則返回給委託的發起者,由它到指定的文件系統或網絡等URL中加載該類。若是它們都沒有加載到這個類時,則拋出ClassNotFoundException異常。不然將這個找到的類生成一個類的定義,並將它加載到內存當中,最後返回這個類在內存中的Class實例對象。jvm

 

二、爲何要使用雙親委託這種模型呢?

       由於這樣能夠避免重複加載,當父親已經加載了該類的時候,就沒有必要子ClassLoader再加載一次。考慮到安全因素,咱們試想一下,若是不使用這種委託模式,那咱們就能夠隨時使用自定義的String來動態替代java核心api中定義的類型,這樣會存在很是大的安全隱患,而雙親委託的方式,就能夠避免這種狀況,由於String已經在啓動時就被引導類加載器(Bootstrcp ClassLoader)加載,因此用戶自定義的ClassLoader永遠也沒法加載一個本身寫的String,除非你改變JDK中ClassLoader搜索類的默認算法。ide

 

三、 可是JVM在搜索類的時候,又是如何斷定兩個class是相同的呢?

     JVM在斷定兩個class是否相同時,不只要判斷兩個類名是否相同,並且要判斷是否由同一個類加載器實例加載的。只有二者同時知足的狀況下,JVM才認爲這兩個class是相同的。就算兩個class是同一份class字節碼,若是被兩個不一樣的ClassLoader實例所加載,JVM也會認爲它們是兩個不一樣class。

 

四、ClassLoader的體系架構:

 

3、自定義ClassLoader,自定義ClassLoader須要繼承java.lang.ClassLoader或者繼承URLClassLoader

放兩個類型的具體實現代碼:

1.繼承自ClassLoader

public class NetworkClassLoader extends ClassLoader {  
      
    private String rootUrl;  
  
    public NetworkClassLoader(String rootUrl) {  
        this.rootUrl = rootUrl;  
    }  
  
    @Override  
    protected Class<?> findClass(String name) throws ClassNotFoundException {  
        Class clazz = null;//this.findLoadedClass(name); // 父類已加載     
        //if (clazz == null) {  //檢查該類是否已被加載過  
            byte[] classData = getClassData(name);  //根據類的二進制名稱,得到該class文件的字節碼數組  
            if (classData == null) {  
                throw new ClassNotFoundException();  
            }  
            clazz = defineClass(name, classData, 0, classData.length);  //將class的字節碼數組轉換成Class類的實例  
        //}   
        return clazz;  
    }  
  
    private byte[] getClassData(String name) {  
        InputStream is = null;  
        try {  
            String path = classNameToPath(name);  
            URL url = new URL(path);  
            byte[] buff = new byte[1024*4];  
            int len = -1;  
            is = url.openStream();  
            ByteArrayOutputStream baos = new ByteArrayOutputStream();  
            while((len = is.read(buff)) != -1) {  
                baos.write(buff,0,len);  
            }  
            return baos.toByteArray();  
        } catch (Exception e) {  
            e.printStackTrace();  
        } finally {  
            if (is != null) {  
               try {  
                  is.close();  
               } catch(IOException e) {  
                  e.printStackTrace();  
               }  
            }  
        }  
        return null;  
    }  
  
    private String classNameToPath(String name) {  
        return rootUrl + "/" + name.replace(".", "/") + ".class";  
    }  
  
}  

 

2.繼承自URLClassLoader

ublic class SimpleURLClassLoader extends URLClassLoader {
    //工程class類所在的路徑
    public static String projectClassPath = "E:/IDE/work_place/ZJob-Note/bin/";
    //全部的測試的類都在同一個包下
    public static String packagePath = "testjvm/testclassloader/";

    public SimpleURLClassLoader() {
        //設置ClassLoader加載的路徑
        super(getMyURLs());
    }

    private static  URL[] getMyURLs(){
        URL url = null;
        try {
            url = new File(projectClassPath).toURI().toURL();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return new URL[] { url };
    }

    public Class load(String name) throws Exception{
        return loadClass(name);
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name,false);
    }

    /**
     * 重寫loadClass,不採用雙親委託機制("java."開頭的類仍是會由系統默認ClassLoader加載)
     */
    @Override
    public Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
        Class clazz = null;
        //查看HotSwapURLClassLoader實例緩存下,是否已經加載過class
        clazz = findLoadedClass(name);
        if (clazz != null ) {
            if (resolve) {
                resolveClass(clazz);
            }
            return (clazz);
        }

        //若是類的包名爲"java."開始,則有系統默認加載器AppClassLoader加載
        if(name.startsWith("java.")) {
            try {
                //獲得系統默認的加載cl,即AppClassLoader
                ClassLoader system = ClassLoader.getSystemClassLoader();
                clazz = system.loadClass(name);
                if (clazz != null) {
                    if (resolve)
                        resolveClass(clazz);
                    return (clazz);
                }
            } catch (ClassNotFoundException e) {
                // Ignore
            }
        }      
        return customLoad(name,this);
    } 
    /**
     * 自定義加載
     * @param name
     * @param cl 
     * @return
     * @throws ClassNotFoundException
     */
    public Class customLoad(String name,ClassLoader cl) throws ClassNotFoundException {
        return customLoad(name, false,cl);
    }

    /**
     * 自定義加載
     * @param name
     * @param resolve
     * @return
     * @throws ClassNotFoundException
     */
    public Class customLoad(String name, boolean resolve,ClassLoader cl) throws ClassNotFoundException {
        //findClass()調用的是URLClassLoader裏面重載了ClassLoader的findClass()方法
        Class clazz = ((SimpleURLClassLoader)cl).findClass(name);
        if (resolve)
            ((SimpleURLClassLoader)cl).resolveClass(clazz);
        return clazz;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        return super.findClass(name);
    }
}

 

4、ClassLoader卸載Class

JVM中的Class只有知足如下三個條件,才能被GC回收,也就是該Class被卸載(unload):

  • 該類全部的實例都已經被GC。
  • 加載該類的ClassLoader實例已經被GC。
  • 該類的java.lang.Class對象沒有在任何地方被引用。

GC的時機咱們是不可控的,那麼一樣的咱們對於Class的卸載也是不可控的。

package testjvm.testclassloader;
public class TestClassUnLoad {
    public static void main(String[] args) throws Exception {
        SimpleURLClassLoader loader = new SimpleURLClassLoader();
        // 用自定義的加載器加載A
        Class clazzA = loader.load("testjvm.testclassloader.A");
        Object a = clazzA.newInstance();
        // 清除相關引用
        a = null;  //清除該類的實例
        clazzA = null;  //清除該class對象的引用
        loader = null;  //清楚該類的ClassLoader引用
        // 執行一次gc垃圾回收
        System.gc();
        System.out.println("GC over");
    }
}

 

參考文檔:

http://blog.csdn.net/xyang81/article/details/7292380

https://my.oschina.net/xianggao/blog/367822 

相關文章
相關標籤/搜索