Java --ClassLoader創建、載入class、卸載class

来源:http://www.cnblogs.com/eoss/archive/2016/10/24/5992499.html
-Advertisement-
Play Games

一、java提供了三種ClassLoader對Class進行載入: 1.BootStrap ClassLoader:稱為啟動類載入器,是Java類載入層次中最頂層的類載入器,負責載入JDK中的核心類庫,如:rt.jar、resources.jar、charsets.jar等,可通過如下程式獲得該類加 ...


一、java提供了三種ClassLoader對Class進行載入:

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());  
}  

或者

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文件。

 

二、ClassLoader的載入原理

 1、原理介紹

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

 

2、為什麼要使用雙親委托這種模型呢?

       因為這樣可以避免重覆載入,當父親已經載入了該類的時候,就沒有必要子ClassLoader再載入一次。考慮到安全因素,我們試想一下,如果不使用這種委托模式,那我們就可以隨時使用自定義的String來動態替代java核心api中定義的類型,這樣會存在非常大的安全隱患,而雙親委托的方式,就可以避免這種情況,因為String已經在啟動時就被引導類載入器(Bootstrcp ClassLoader)載入,所以用戶自定義的ClassLoader永遠也無法載入一個自己寫的String,除非你改變JDK中ClassLoader搜索類的預設演算法。

 

3、 但是JVM在搜索類的時候,又是如何判定兩個class是相同的呢?

     JVM在判定兩個class是否相同時,不僅要判斷兩個類名是否相同,而且要判斷是否由同一個類載入器實例載入的。只有兩者同時滿足的情況下,JVM才認為這兩個class是相同的。就算兩個class是同一份class位元組碼,如果被兩個不同的ClassLoader實例所載入,JVM也會認為它們是兩個不同class。

 

4、ClassLoader的體系架構:

 

三、自定義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);
    }
}

 

四、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 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 概述 在進行ESB集成項目中,使用到了很多系統的介面,這些介面傳輸的數據大部分都採用了XML的格式,這樣在使用ESB開發服務時就需要對XML數據進行解析或拼接的操作,本文以項目中流程服務為例,講解一些常用的Dom4j對XML的操作。 2 名詞解釋 Dom4j:一個Java的XML API,用來讀 ...
  • 做python項目,需要用到mysql,一般用python-mysql,安裝時遇到錯誤提示如下: Command "python setup.py egg_info" failed with error code 1 Trace的關鍵信息是:sh: mysql_config: command not ...
  • 測試在進行一次性能測試的時候發現併發300個請求時出現了下麵的異常: HTTP Status 500 - Handler processing failed; nested exception is java.lang.OutOfMemoryError: unable to create new n ...
  • 【轉盤游戲】有一個轉盤,26個英文字母均勻分佈在轉盤邊緣,轉針初始位置在字母a,每次只能轉動到相鄰的字母,然後輸入一個由26個字母組成的字元串(最長100),不複位地依次轉動到相應的字母位置,問:一共需要轉多少次?編程實現。 Input:   輸入一個字元串,最長100,由26個英文字母組成。... ...
  • 前言: 我們在做自然語言學習的過程中使用Python進行編程。是用解析器的方式確實有些麻煩。在這裡給大家推薦一款集成開發環境IDE可以很方便的對Python進行項目管理,代碼自動提示,運行調試等。 簡介: 這裡不做其它IDE的推薦,直接推薦pycharm。它是大名頂頂的JetBrains公司出品的基 ...
  • 來源: POJ 註意: 總時間限制: 1000ms 記憶體限制: 65536kB 描述 魔獸世界的西面是紅魔軍的司令部,東面是藍魔軍的司令部。兩個司令部之間是依次排列的若幹城市。 紅司令部,City 1,City 2,……,City n,藍司令部 兩軍的司令部都會製造武士。武士一共有 dragon 、 ...
  • 關於結構體的詳細分析 只定義結構體 是結構體的名字 定義結構體變數 定義結構體並同時定義結構體變數 關於指針的詳細分析 定義指針變數: 定義指針的指針變數: 賦初值: 關於 和`int p`區別: 如果是c,我推薦 這樣的寫法因為變數定義需要放在函數開始的地方. 如果是c++,我推薦 分行寫並初始化 ...
  • 程式名稱: 選課系統 角色:學校、學員、課程、講師要求:1. 創建北京、上海 2 所學校2. 創建linux , python , go 3個課程 , linux\py 在北京開, go 在上海開3. 課程包含,周期,價格,通過學校創建課程 4. 通過學校創建班級, 班級關聯課程、講師5. 創建學員 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...