1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        虛擬機(jī)類加載機(jī)制

        共 5483字,需瀏覽 11分鐘

         ·

        2020-11-24 13:22

        PS:雖然最近更新頻率低了,但是思危一直沒有停止,共勉!
        今天介紹一下 JVM 類加載器機(jī)制,主要內(nèi)容如下:
        1. 概述

        2. 類加載的時(shí)機(jī)

        3. 類加載的過程

        4. 類加載器

        5. 類加載器分類

        6. 雙親委托模型

        概述

        JVM 把字節(jié)碼(.class)文件加載到內(nèi)存中,并對數(shù)據(jù)進(jìn)行校驗(yàn)、解析和初始化,最終生成可以被 JVM 直接使用的 Java 類型,這就是 JVM 的類加載機(jī)制。
        在 Java 中各種類型的加載、連接和初始化過程都是在程序運(yùn)行期間完成的,這種方式會(huì)在類加載時(shí)帶來一些性能開銷,但是具有很高的靈活性,Java 的動(dòng)態(tài)擴(kuò)展的語言特性就是依賴運(yùn)行期間動(dòng)態(tài)加載和動(dòng)態(tài)鏈接這個(gè)特點(diǎn)實(shí)現(xiàn)的,如插件化技術(shù)中通過自定義類加載器實(shí)現(xiàn)資源的加載和替換,其中就是用的 Java 語言運(yùn)行期間類加載的特性。

        類加載的時(shí)機(jī)

        類從被加載到 JVM 內(nèi)存中開始,一直到從 JVM 內(nèi)存中卸載為止,類加載的生命周期如下圖所示:
        加載、驗(yàn)證、準(zhǔn)備、初始化、卸載這五個(gè)階段的順序是確定的,類的解析則不一定,可能會(huì)在初始化之后再進(jìn)行,這是為了支持 Java 語言的運(yùn)行時(shí)綁定,在整個(gè)類加載的過程中,每一個(gè)階段都由前一個(gè)階段觸發(fā)進(jìn)行。
        JVM 規(guī)范中規(guī)定了類的初始化階段,但是加載這個(gè)階段沒有進(jìn)行約束,具體由 JVM 實(shí)現(xiàn)自己控制,當(dāng)然加載、驗(yàn)證、準(zhǔn)備必須在初始化這個(gè)階段之前完成。
        那么什么情況下類開始初始化呢,JVM 嚴(yán)格規(guī)定了下面這些情況必須對類進(jìn)行初始化:
        1. 遇到 new、getstatic/putstatic、invokestatic 指令時(shí),如果該類沒有被初始化,則需對類進(jìn)行初始化,上面指令分別對應(yīng)使用 new 關(guān)鍵字進(jìn)行對象實(shí)例化、讀取或設(shè)置一個(gè)靜態(tài)屬性、調(diào)用靜態(tài)方法,具體可以使用 javap 命令查看字節(jié)碼文件的實(shí)現(xiàn)來驗(yàn)證;

        2. 使用 java,lang.reflect 對類進(jìn)行反射調(diào)用的時(shí)候,如果該類沒有被初始化,則需對類進(jìn)行初始化;

        3. 當(dāng)初始化一個(gè)類的時(shí)候,如果其父類還沒有進(jìn)行初始化,則先進(jìn)行該類父類的初始化;

        4. 當(dāng) JVM 啟動(dòng)時(shí),用戶指定要啟動(dòng)的主類,比如還有 main 方法的類,JVM 會(huì)先初始化這個(gè)類;

        5. 當(dāng)使用 JDK 1.7 的動(dòng)態(tài)語言支持時(shí),如果 java.lang.invoke.MethodHandler 實(shí)例最后解析結(jié)果是 REF_getStatic、REF_putStatic、REF_invokeStatic,如果這些句柄對應(yīng)的類沒有進(jìn)行初始化,則需先對其進(jìn)行初始化,至于 MethodHandler ?可以理解為反射的另一種形式。

        類加載的過程

        下面針對類加載的幾個(gè)階段進(jìn)行具體說明。
        加載
        class 文件通過類加載器將其中的字節(jié)碼內(nèi)容加載到內(nèi)存中,將這個(gè)靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu),在堆內(nèi)存中生成該 class 文件對應(yīng)的 java.lang.Class 對象,這個(gè) Class 對象就是訪問方法區(qū)類數(shù)據(jù)的入口。
        JVM 規(guī)范并沒有規(guī)定 class 文件的來源,舉例如下:
        • 從 zip 包中獲取,最終成為 jar、war 格式的基礎(chǔ)。

        • 從網(wǎng)絡(luò)中獲取,典型應(yīng)用就是 Applet。

        • 運(yùn)行時(shí)生成,典型應(yīng)用就是動(dòng)態(tài)代理技術(shù),在 java.lang.reflect.Proxy 中就是使用 ProxyGenerator.generatrProxyClass 來為特定接口生成形如 Proxy 的代理類的二進(jìn)制字節(jié)流。

        • 其他文件生成、數(shù)據(jù)庫中獲取等。

        類的加載階段與后面的鏈接階段的過程是交叉進(jìn)行的,沒有明確的界限,加載階段尚未完成,鏈接階段可能已經(jīng)開始,但是兩個(gè)階段的開始時(shí)間還是保持著固定的先后順序。
        鏈接
        鏈接包括驗(yàn)證、準(zhǔn)備、解析三個(gè)階段,
        • 驗(yàn)證:確保 class 文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,且不會(huì)危害虛擬機(jī)自身的安全,從整體來看,驗(yàn)證階段主要包括文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證,具體驗(yàn)證內(nèi)容可以自行查看 Java 虛擬機(jī)規(guī)范。

        • 準(zhǔn)備:正式為類變量分配內(nèi)存并設(shè)置類變量的初始值,這個(gè)初始值一般是數(shù)據(jù)類型的初始值,而不是真正代碼中初始化的值,如 int 初始值就是 0,這些類變量使用的內(nèi)存都將在方法區(qū)進(jìn)行分配,類變量指的就是被 static 關(guān)鍵字修飾過的變量。

        • 解析:JVM 將常量池中的符號(hào)引用替換為直接引用,這里的符號(hào)引用就是在前面驗(yàn)證階段提到的符號(hào)引用驗(yàn)證中的符號(hào)引用

        初始化
        類初始化階段是類加載階段的最后一步,前面的加載階段、鏈接階段除了用戶自定義類加載其參與外,其余操作都是由 JVM 來完成的,初始化階段才真正開始執(zhí)行 Java 代碼,也就是字節(jié)碼,關(guān)于類的初始化可以了解一下幾點(diǎn):
        1. 初始化階段就是執(zhí)行類構(gòu)造器 () 方法的過程。

        2. <clinit>() 方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語句塊 statuc{} 中的語句合并產(chǎn)生的,編譯器收集順序和源碼中語句順序一致,如靜態(tài)語句塊中只能訪問定義在它之前的變量,定義在它后面的變量只能復(fù)制不能訪問。
        3. 初始化一個(gè)類時(shí),如果父類還沒初始化,則先進(jìn)行父類的初始化。

        4. JVM 會(huì)保證一個(gè)類的 () 方法在多線程環(huán)境中被正確的加鎖、同步。

        5. 當(dāng)訪問一個(gè) Java 類的靜態(tài)域時(shí),只有真正聲明這個(gè)類才會(huì)被初始化。

        類加載器

        顧名思義,類加載器(class loader) 用來加載 Java 類到 JVM 中的,所有的類加載器都是 java.lang.ClassLoader 類的一個(gè)實(shí)例,前面知道在類的加載階段會(huì)通過類加載器來加載 class 文件,也就是可以通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流 ,這個(gè)動(dòng)作的代碼實(shí)現(xiàn)就是類加載器的實(shí)現(xiàn)。
        對于任意一個(gè)類,都需要加載它的類加載器和這個(gè)類本身一同確立其在 JVM 中的唯一性,每個(gè)類加載器都擁有獨(dú)立的類名稱空間,也就是說,兩個(gè)相同的類被不同的類加載器加載后將不再相等。

        類加載器分類

        從 JVM 的角度來說,只存在兩種不同的類加載器:
        1. 啟動(dòng)類加載器(Bootstrap ClassLoader):一般使用 C++ 語言實(shí)現(xiàn),具體由 JVM 實(shí)現(xiàn)。

        2. 其他類加載器:使用 Java 語言實(shí)現(xiàn),獨(dú)立于 JVM 之外,且都是 java.lang.ClassLoader 的一個(gè)實(shí)例,如 Android 中的 DexClassLoader。

        從 Java 開發(fā)人員的角度來說,類加載器可以分為三類:
        1. 啟動(dòng)類加載器(Bootstrap ClassLoader):負(fù)責(zé)加載的是 JAVA_HOME\lib 下的類庫,或者被-Xbootclasspath 參數(shù)所指定的路徑中的并且是 JVM 識(shí)別的(僅按照文件名識(shí)別,如 rt.jar 等,名字不符合的類庫即使放在 lib 目錄中也不會(huì)被加載),啟動(dòng)類加載器無法被JAVA程序直接應(yīng)用。

        2. 擴(kuò)展類加載器(Extension ClassLoader):這個(gè)類加載器由 sum.misc.Launcher$ExtClassLoader 實(shí)現(xiàn),負(fù)責(zé)加載 JAVA_HOME\lib\ext 下的類,或者是被 java.ext.dirs 系統(tǒng)變量所指定的路徑下的類庫,可以直接使用擴(kuò)展類加載器。

        3. 應(yīng)用程序類加載器(Application ClassLoader):這個(gè)類加載器由 sun.misc.Launcher$AppClassLoader 實(shí)現(xiàn),這個(gè)類加載器是 ClassLoader 中的getSystemClassLoader() 方法的返回值, 一般也稱它為系統(tǒng)類加載器,負(fù)責(zé)加載用戶類路徑(ClassPath) 下所指定的類庫, 開發(fā)者可以直接使用這個(gè)類加載器, 如果應(yīng)用程序中沒有自定義過自己的類加載器, 這個(gè)就是程序中默認(rèn)的類加載器。

        雙親委托模型

        先來看一下上面類加載器的關(guān)系:
        上圖中展示的類加載器之間的這種層次關(guān)系, 稱為類加載器的雙親委派模型( Parents Delegation Model),雙親委派模型要求除了頂層的啟動(dòng)類加載器外, 其余的類加載器都應(yīng)當(dāng)有自己的父類加載器, 這里類加載器之間的父子關(guān)系一般不會(huì)以繼承(Inheritance) 的關(guān)系
        來實(shí)現(xiàn), 而是都使用組合(Composition) 關(guān)系來復(fù)用父加載器的代碼,這種方式并不是一個(gè)強(qiáng)制性的約束模型, 而是 Java 設(shè)計(jì)者推薦給開發(fā)者的一種類加載器實(shí)現(xiàn)方式。
        那么雙親委托模型的工作流程是什么呢?
        當(dāng)一個(gè)類加載器收到類加載的請求,首先不會(huì)去加載這個(gè)類,而是把這個(gè)類加載的請求委托給父類類加載器,以此類推,最終每個(gè)類加載的請求都會(huì)委托給啟動(dòng)類加載器,只有當(dāng)父類類加載器無法完成該類的加載,子類加載器才會(huì)嘗試自己去加載,加載過程參考如下:
        1protected?Class?loadClass(String?name,?boolean?resolve)throws?ClassNotFoundException{
        2????synchronized?(getClassLoadingLock(name))?{
        3????????//?1.?檢查是否已經(jīng)加載過類
        4????????Class?c?=?findLoadedClass(name);
        5????????if?(c?==?null)?{
        6????????????long?t0?=?System.nanoTime();
        7????????????try?{
        8????????????????if?(parent?!=?null)?{
        9????????????????????//?2.?如果沒有被加載,則調(diào)用父類的類加載器加載
        10????????????????????c?=?parent.loadClass(name,?false);
        11????????????????}?else?{
        12????????????????????//?3.?如果父類的類加載器不存在,則直接使用啟動(dòng)類加載器進(jìn)行加載
        13????????????????????c?=?findBootstrapClassOrNull(name);
        14????????????????}
        15????????????}?catch?(ClassNotFoundException?e)?{
        16????????????????//?ClassNotFoundException?thrown?if?class?not?found
        17????????????????//?from?the?non-null?parent?class?loader
        18????????????}
        19
        20????????????if?(c?==?null)?{
        21????????????????//?If?still?not?found,?then?invoke?findClass?in?order
        22????????????????//?to?find?the?class.
        23????????????????long?t1?=?System.nanoTime();
        24????????????????//?4.?父類或啟動(dòng)類加載器都沒有加載該類,則調(diào)用自己的也就是子類類加載器的findClass犯法進(jìn)行類加載
        25????????????????c?=?findClass(name);
        26????????????????sun.misc.PerfCounter.getParentDelegationTime().addTime(t1?-?t0);
        27????????????????sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
        28????????????????sun.misc.PerfCounter.getFindClasses().increment();
        29????????????}
        30????????}
        31????????if?(resolve)?{
        32????????????resolveClass(c);
        33????????}
        34????????return?c;
        35????}
        36}
        JDK 1.2 之后的 java.lang.ClassLoader 添加了一個(gè)新的 protected 方法 findClass() ,如果要自定義類加載器,則直接實(shí)現(xiàn) findClass() 方法即可而不必重寫 ?loadClass() 方法,因?yàn)?loadClass() 方法最終調(diào)用了 findClass() 方法,這樣自定義的類加載器就是符合雙親委托規(guī)則的。
        前面介紹了 JVM 類加載機(jī)制以及類加載器的相關(guān)知識(shí),類加載器很好的支持了 Java 的動(dòng)態(tài)擴(kuò)展特性,在 Android 中也有使用,如在插件化技術(shù)中用到的 PathClassLoader、DexClassLoader 都是 ClassLoader 的間接子類,正是這種對 class 文件來源沒有進(jìn)行限制,基于此可以實(shí)現(xiàn) App 的插件化。
        推薦閱讀:
        瀏覽 45
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            91在线黄色视频 | 一级a性色生活片久久毛片 | 成人福利小视频 | 日本乱大交xxxxx | 宝贝腿开大点我添添摸摸免费视频 | 少妇放荡的呻吟干柴烈火动态图 | 性视频日韩 | 精品三级片 | 免费在线观看a级片 | 免费 成人 学 |