91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中的類動態加載和熱替換如何使用

發布時間:2022-02-28 10:02:52 來源:億速云 閱讀:138 作者:iii 欄目:開發技術

這篇文章主要講解了“Java中的類動態加載和熱替換如何使用”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java中的類動態加載和熱替換如何使用”吧!

    前言

    最近,遇到了兩個和Java類的加載和卸載相關的問題:

    1) 是一道關于Java的判斷題:一個類被首次加載后,會長期留駐JVM,直到JVM退出。這個說法,是不是正確的?

    2) 在開發的一個集成平臺中,需要集成類似接口的多種工具,并且工具可能會有新增,同時在不同的環境部署會有裁剪(例如對外提供服務的應用,不能提供特定的采購的工具),如何才能更好地實現?

    針對上面的第2點,我們采用Java插件化開發實現。上面的兩個問題,都和Java的類加載和熱替換機制有關。

    1. Java的類加載器和雙親委派模型

    1.1 Java類加載器

    類加載器,顧名思義,就是用來實現類的加載操作。每個類加載器都有一個獨立的類名稱空間,就是說每個由該類加載器加載的類,都在自己的類名稱空間,如果要比較兩個類是否“相等”,首先這兩個類必須在相同的類命名空間,即由相同的類加載器加載(即對于任何一個類,都必須由該類本身和加載它的類加載器一起確定其在JVM中的唯一性),不是同一個類加載器加載的類,不會相等。

    在Java中,主要有如下的類加載器:

    Java中的類動態加載和熱替換如何使用

    下面,簡單介紹上面這幾種類加載器:

    • 啟動類加載器(Bootstrap Class Loader):這個類使用C++開發(所有的類加載器中,唯一使用C++開發的類加載器),用來加載<JAVA_HOME>/lib目錄中jar和tools.jar或者使用 -Xbootclasspath 參數指定的類。

    • 擴展類加載器(Extension Class Loader):定義為misc.Launcher$ExtClassLoader,用來加載<JAVA_HOME>/lib/ext目錄或者使用java.ext.dir指定的類。

    • 應用程序類加載器(Application Class Loader):定義為misc.Launcher$AppClassLoader,用來加載用戶類路徑下面(classpath)下面所有的類,一般情況下,該類是應用程序默認的類加載器。

    • 用戶自定義類加載器(User Class Loader):用戶自定義類加載器,一般沒有必要,后面我們會專門來一部分介紹該類型的類加載器。

    1.2 雙親委派模型

    雙親委派模型,是從 Java1.2 開始引入的一種類加載器模式,在Java中,類的加載操作通過java.lang.ClassLoader中的loadClass()方法完成,咱們首先看看該方法的實現(直接從Java源碼中撈出來的):

    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 {
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                        // ClassNotFoundException thrown if class not found
                        // from the non-null parent class loader
                    }
    
                    if (c == null) {
                        // 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;
            }
        }

    我們結合上面的注釋,來解釋下雙親委派模型的內容:

    1) 接收到一個類加載請求后,首先判斷該類是否有加載,如果已經加載,則直接返回;

    2) 如果尚未加載,首先獲取父類加載器,如果可以獲取父類加載器,則調用父類的loadClass()方法來加載該類,如果無法獲取父類加載器,則調用啟動器加載器來加載該類;

    3) 判斷該類是否被父類加載器或者啟動類加載器加載,如果已經加載完成則返回,如果未成功加載,則自己嘗試來加載該類。

    上面的描述,說明了loadClass()方法的實現,我們進一步對上面的步驟進行解釋:

    • 因為類加載器首先調父類加載器來進行加載,從loadClass()方法的實現,我們知道父類加載器會嘗試調自己的父類加載器,直到啟動類加載器,所以,任何一個類的加載,都會最終委托到啟動類加載器來首先加載;

    • 在前面有進行介紹,啟動類加載器、擴展類加載器、應用程序類加載器,都有自己加載的類的范圍,例如啟動類加載器只加載JDK核心庫,因此并不是父類加載器就可以都加載成功,父類加載器無法加載(一般如上面代碼,拋出來ClassNotFoundException),此時會由自己加載。

    最后啰嗦一下,再進行一下總結:

    雙親委派模型:如果一個類加載器收到類加載請求,會首先把加載請求委派給父類加載器完成,每個層次的類加載器都是這樣,最終所有的加載請求都傳動到最根的啟動類加載器來完成,如果父類加載器無法完成該加載請求(即自己加載的范圍內找不到該類),子類加載器才會嘗試自己加載。

    這樣的雙親委派模型有個好處:就是所有的類都盡可能由頂層的類加載器加載,保證了加載的類的唯一性,如果每個類都隨機由不同的類加載器加載,則類的實現關系無法保證,對于保證Java程序的穩定運行意義重大。

    2. Java的類動態加載和卸載

    2.1 Java類的卸載

    在Java中,每個類都有相應的Class Loader,同樣的,每個實例對象也會有相應的類,當滿足如下三個條件時,JVM就會卸載這個類:

    1) 該類所有實例對象不可達

    2) 該類的Class對象不可達

    3) 該類的Class Loader不可達

    那么,上面示例對象、Class對象和類的Class Loader直接是什么關系呢?

    在類加載器的內部實現中,用一個Java集合來存放所加載類的引用。而一個Class對象總是會引用它的類加載器,調用Class對象的getClassLoader()方法,就能獲得它的類加載器。所以,Class實例和加載它的加載器之間為雙向引用關系。

    一個類的實例總是引用代表這個類的Class對象。在Object類中定義了getClass()方法,這個方法返回代表對象所屬類的Class對象的引用。此外,所有的Java類都有一個靜態屬性class,它引用代表這個類的Class對象。

    Java虛擬機自帶的類加載器(前面介紹的三種類加載器)在JVM運行過程中,會始終存在,而這些類加載器則會始終引用它們所加載的類的Class對象,因此這些Class對象始終是可觸及的。因此,由Java虛擬機自帶的類加載器所加載的類,在虛擬機的生命周期中,始終不會被卸載。

    那么,我們是不是就完全不能在Java程序運行過程中,動態修改我們使用的類了嗎?答案是否定的!根據上面的分析,通過Java虛擬機自帶的類加載器加載的類無法卸載,我們可以自定義類加載器來加載Java程序,通過自定義類加載器加載的Java類,是可以被卸載的。

    2.2 自定義類加載器

    前面介紹到,類加載的雙親委派模型,是推薦模型,在loadClass中實現的,并不是必須使用的模型。我們可以通過自定義類加載器,直接加載我們需要的Java類,而不委托給父類加載器。

    Java中的類動態加載和熱替換如何使用

    如上圖所示,我們有自定義的類加載器MyClassLoader,用來加載類MyClass,則在JVM中,會存在上面三類引用(上圖忽略這三種類型對象對其他的對象的引用)。如果我們將左邊的三個引用變量,均設置為null,那么此時,已經加載的MyClass將會被卸載。

    2.3 動態卸載存在的問題

    動態卸載需要借助于JVM的垃圾收集功能才可以做到,但是我們知道,JVM的垃圾回收,只有在堆內存占用比較高的時候,才會觸發。即使我們調用了System.gc(),也不會立即執行垃圾回收操作,而只是告訴JVM需要執行垃圾回收,至于什么時候垃圾回收,則要看JVM自己的垃圾回收策略。

    但是我們不需要悲觀,即使動態卸載不是那么牢靠,但是實現動態的Java類的熱替換還是有希望的。

    3. Java類的熱替換

    下面通過代碼來介紹Java類的熱替換方法(代碼簡陋,主要為了說明問題):

    如下面的代碼:

    首先定義一個自定義類加載器:

    package zmj;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class FileClassLoader extends ClassLoader {
        private String fileName;
    
        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
    
        public Class loadClass(String name) throws ClassNotFoundException {
            if (name.startsWith("java")) {
                return getSystemClassLoader().loadClass(name);
            }
            Class cls = null;
            File classF = new File(fileName);
            try {
                cls = instantiateClass(name, new FileInputStream(classF), classF.length());
            } catch (IOException e) {
                e.printStackTrace();
            }
            return cls;
        }
    
        private Class instantiateClass(String name, InputStream fin, long len) throws IOException {
            byte[] raw = new byte[(int) len];
            fin.read(raw);
            fin.close();
            return defineClass(name, raw, 0, raw.length);
        }
    }

    上面在loadClass時,先判斷類name(包含package的全限定名)是否以java開始,如果是java開始,則使用JVM自帶的類加載器加載。

    然后定義一個簡單的動態加載類:

    package zmj;
    
    public class SayHello {
        public void say() {
            System.out.println("hello ping...");
        }
    }

    在執行過程中,會動態修改打印內容,測試類的熱加載。

    然后定義一個調用類:

    package zmj;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public class Main {
        public static void main(String[] args) throws InterruptedException, ClassNotFoundException,
                IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
            while (true) {
                FileClassLoader fileClassLoader = new FileClassLoader();
                fileClassLoader.setFileName("D:/workspace/idea/test/class-loader-test/target/classes/zmj/SayHello.class");
                Object obj = null;
                obj = fileClassLoader.loadClass("zmj.SayHello").newInstance();
                Method m = obj.getClass().getMethod("say", new Class[]{});
                m.invoke(obj, new Object[]{});
                Thread.sleep(2000);
            }
        }
    }

    當我們運行上面Main程序過程中,我們動態修改執行內容(SayHello中,從 hello zmj... 更改為 hello ping...),最終展示的內容如下:

    hello zmj...

    hello zmj...

    hello zmj...

    hello ping...

    hello ping...

    hello ping...

    感謝各位的閱讀,以上就是“Java中的類動態加載和熱替換如何使用”的內容了,經過本文的學習后,相信大家對Java中的類動態加載和熱替換如何使用這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    建瓯市| 疏附县| 嘉黎县| 亚东县| 吴江市| 怀远县| 侯马市| 招远市| 泰和县| 遂平县| 宁城县| 临夏市| 肥城市| 台南县| 邳州市| 六枝特区| 澄迈县| 南昌市| 福海县| 泰兴市| 民和| 定西市| 文化| 房产| 康平县| 武川县| 乌兰察布市| 江安县| 布拖县| 思南县| 荆州市| 鄂州市| 南平市| 永济市| 木里| 方城县| 张家港市| 波密县| 长乐市| 简阳市| 淮安市|