<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          最新版JDK15的JVM類(lèi)加載器詳解

          共 3827字,需瀏覽 8分鐘

           ·

          2021-01-20 22:16


          ? 點(diǎn)擊上方“JavaEdge”,關(guān)注公眾號(hào)

          設(shè)為“星標(biāo)”,好文章不錯(cuò)過(guò)!


          1 類(lèi)加載器


          在類(lèi)加載器家族中存在著類(lèi)似人類(lèi)社會(huì)的權(quán)力等級(jí)制度:



          1.1?Bootstrap


          由C/C++實(shí)現(xiàn),啟動(dòng)類(lèi)加載器,屬最高層,JVM啟動(dòng)時(shí)創(chuàng)建,通常由與os相關(guān)的本地代碼實(shí)現(xiàn),是最根基的類(lèi)加載器。

          JDK8 時(shí)


          需要注意的是,Bootstrap ClassLoader智慧加載特定名稱(chēng)的類(lèi)庫(kù),比如rt.jar.這意味我們自定義的jar扔到\jre\lib也不會(huì)被加載.

          負(fù)責(zé)將/jre/lib- Xbootclasspath參數(shù)指定的路徑中的,且是虛擬機(jī)識(shí)別的類(lèi)庫(kù)加載到內(nèi)存中(按照名字識(shí)別,比如rt.jar,對(duì)于不能識(shí)別的文件不予裝載),比如:

          • Object

          • System

          • String

          • Java運(yùn)行時(shí)的rt.jar等jar包

          • 系統(tǒng)屬性sun.boot.class.path指定的目錄中特定名稱(chēng)的jar包

          在JVM啟動(dòng)時(shí),通過(guò)Bootstrap ClassLoader加載rt.jar,并初始化sun.misc.Launcher從而創(chuàng)建Extension ClassLoaderApplication ClassLoader的實(shí)例。
          查看Bootstrap ClassLoader到底初始化了那些類(lèi)庫(kù):

          URL[] urLs = Launcher.getBootstrapClassPath().getURLs();       for (URL urL : urLs) {           System.out.println(urL.toExternalForm());       }

          JDK9 后


          負(fù)責(zé)加載啟動(dòng)時(shí)的基礎(chǔ)模塊類(lèi),比如:

          • java.base

          • java.management

          • java.xml




          1.2?Platform ClassLoader


          JDK8 時(shí)Extension ClassLoader


          只有一個(gè)實(shí)例,由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn):

          • 負(fù)責(zé)加載\lib\extjava.ext.dirs系統(tǒng)變量指定的路徑中的所有類(lèi)庫(kù)

          • 加載一些擴(kuò)展的系統(tǒng)類(lèi),比如XML、加密、壓縮相關(guān)的功能類(lèi)等


          JDK9時(shí)替換為平臺(tái)類(lèi)加載器


          加載一些平臺(tái)相關(guān)的模塊,比如java.scriptingjava.compiler*、?java.corba*

          那為何 9 時(shí)廢除替換了呢?

          JDK8 的主要加載 jre lib 的ext,擴(kuò)展 jar 包時(shí)使用,這樣操作并不推薦,所以廢除。而 JDK9 有了模塊化,更無(wú)需這種擴(kuò)展加載器。



          1.3?Application ClassLoader


          只有一個(gè)實(shí)例,由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)。

          JDK8 時(shí)


          負(fù)責(zé)加載系統(tǒng)環(huán)境變量ClassPath或者系統(tǒng)屬性java.class.path指定目錄下的所有類(lèi)庫(kù)。
          如果應(yīng)用程序中沒(méi)有定義自己的加載器,則該加載器也就是默認(rèn)的類(lèi)加載器。該加載器可以通過(guò)java.lang.ClassLoader.getSystemClassLoader獲取。

          JDK9 后


          應(yīng)用程序類(lèi)加載器,用于加載應(yīng)用級(jí)別的模塊,比如:

          • jdk.compiler

          • jdk.jartool

          • jdk.jshell

          • classpath路徑中的所有類(lèi)庫(kù)

          第二、三層類(lèi)加載器為Java語(yǔ)言實(shí)現(xiàn),用戶(hù)也可以



          1.4 自定義類(lèi)加載器

          用戶(hù)自定義的加載器,是java.lang.ClassLoader的子類(lèi),用戶(hù)可以定制類(lèi)的加載方式;只不過(guò)自定義類(lèi)加載器其加載的順序是在所有系統(tǒng)類(lèi)加載器的最后。



          1.5 Thread Context ClassLoader


          每個(gè)線(xiàn)程都有一個(gè)類(lèi)加載器(jdk 1.2后引入),稱(chēng)之為T(mén)hread Context ClassLoader,如果線(xiàn)程創(chuàng)建時(shí)沒(méi)有設(shè)置,則默認(rèn)從父線(xiàn)程中繼承一個(gè),如果在應(yīng)用全局內(nèi)都沒(méi)有設(shè)置,則所有Thread Context ClassLoader為Application ClassLoader.可通過(guò)Thread.currentThread().setContextClassLoader(ClassLoader)來(lái)設(shè)置,通過(guò)Thread.currentThread().getContextClassLoader()來(lái)獲取.

          線(xiàn)程上下文加載器有什么用?


          該類(lèi)加載器容許父類(lèi)加載器通過(guò)子類(lèi)加載器加載所需要的類(lèi)庫(kù),也就是打破了我們下文所說(shuō)的雙親委派模型。
          這有什么好處呢?
          利用線(xiàn)程上下文加載器,我們能夠?qū)崿F(xiàn)所有的代碼熱替換,熱部署,Android中的熱更新原理也是借鑒如此。

          2 驗(yàn)證類(lèi)加載器




          2.1 查看本地類(lèi)加載器



          在JDK8環(huán)境中,執(zhí)行結(jié)果如下

          AppClassLoader的Parent為Bootstrap,它是通過(guò)C/C++實(shí)現(xiàn)的,并不存在于JVM體系內(nèi),所以輸出為 null。



          類(lèi)加載器的特點(diǎn)


          • 類(lèi)加載器并不需要等到某個(gè)類(lèi)"首次主動(dòng)使用”的時(shí)候才加載它,JVM規(guī)范允許類(lèi)加載器在預(yù)料到某個(gè)類(lèi)將要被使用的時(shí)候就預(yù)先加載它

          • Java程序不能直接引用啟動(dòng)類(lèi)加載器,直接設(shè)置classLoader為null,默認(rèn)就使用啟動(dòng)類(lèi)加載器

          • 如果在加載的時(shí)候.class文件缺失,會(huì)在該類(lèi)首次主動(dòng)使用時(shí)通知LinkageError錯(cuò)誤,如果一直沒(méi)有被使用,就不會(huì)報(bào)錯(cuò)

          • 如果沒(méi)有指定父加載器,默認(rèn)就是啟動(dòng)加載器

          • 每個(gè)類(lèi)加載器都有自己的命名空間,命名空間由該加載器及其所有父加載器所加載的類(lèi)構(gòu)成。不同的命名空間,可以出現(xiàn)類(lèi)的全路徑名相同的情況

          • 運(yùn)行時(shí)包由同一個(gè)類(lèi)加載器的類(lèi)構(gòu)成,決定兩個(gè)類(lèi)是否屬于同一個(gè)運(yùn)行時(shí)包,不僅要看全路徑名是否一樣,還要看定義類(lèi)加載器是否相同。只有屬于同一個(gè)運(yùn)行時(shí)包的類(lèi)才能實(shí)現(xiàn)相互包內(nèi)可見(jiàn)

          低層次的當(dāng)前類(lèi)加載器,不能覆蓋更高層次類(lèi)加載器已經(jīng)加載的類(lèi)
          如果低層次的類(lèi)加載器想加載一個(gè)未知類(lèi),要非常禮貌地向上逐級(jí)詢(xún)問(wèn):“請(qǐng)問(wèn),這個(gè)類(lèi)已經(jīng)加載了嗎?”
          被詢(xún)問(wèn)的高層次類(lèi)加載器會(huì)自問(wèn)兩個(gè)問(wèn)題

          • 我是否已加載過(guò)此類(lèi)

          • 如果沒(méi)有,是否可以加載此類(lèi)

          只有當(dāng)所有高層次類(lèi)加載器在兩個(gè)問(wèn)題的答案均為“否”時(shí),才可以讓當(dāng)前類(lèi)加載器加載這個(gè)未知類(lèi)
          左側(cè)綠色箭頭向上逐級(jí)詢(xún)問(wèn)是否已加載此類(lèi),直至
          Bootstrap ClassLoader,然后向下逐級(jí)嘗試是否能夠加載此類(lèi),如果都加載不了,則通知發(fā)起加載請(qǐng)求的當(dāng)前類(lèi)加載器,準(zhǔn)予加載
          在右側(cè)的三個(gè)小標(biāo)簽里,列舉了此層類(lèi)加載器主要加載的代表性類(lèi)庫(kù),事實(shí)上不止于此

          通過(guò)如下代碼可以查看Bootstrap 所有已加載類(lèi)庫(kù)

          執(zhí)行結(jié)果

          Bootstrap加載的路徑可以追加,不建議修改或刪除原有加載路徑
          在JVM中增加如下啟動(dòng)參數(shù),則能通過(guò)
          Class.forName正常讀取到指定類(lèi),說(shuō)明此參數(shù)可以增加Bootstrap的類(lèi)加載路徑:

          -Xbootclasspath/a:/Users/sss/book/ easyCoding/byJdk11/src

          如果想在啟動(dòng)時(shí)觀(guān)察加載了哪個(gè)jar包中的哪個(gè)類(lèi),可以增加

          -XX:+TraceClassLoading

          此參數(shù)在解決類(lèi)沖突時(shí)非常實(shí)用,畢竟不同的JVM環(huán)境對(duì)于加載類(lèi)的順序并非是一致的
          有時(shí)想觀(guān)察特定類(lèi)的加載上下文,由于加載的類(lèi)數(shù)量眾多,調(diào)試時(shí)很難捕捉到指定類(lèi)的加載過(guò)程,這時(shí)可以使用條件斷點(diǎn)功能
          比如,想查看HashMap的加載過(guò)程,在loadClass處打個(gè)斷點(diǎn),并且在condition框內(nèi)輸入如圖

          JVM如何確立每個(gè)類(lèi)在JVM的唯一性


          類(lèi)的全限定名和加載這個(gè)類(lèi)的類(lèi)加載器的ID

          在學(xué)習(xí)了類(lèi)加載器的實(shí)現(xiàn)機(jī)制后,知道雙親委派模型并非強(qiáng)制模型,用戶(hù)可以自定義類(lèi)加載器,在什么情況下需要自定義類(lèi)加載器呢?

          • 隔離加載類(lèi)
            在某些框架內(nèi)進(jìn)行中間件與應(yīng)用的模塊隔離,把類(lèi)加載到不同的環(huán)境
            比如,阿里內(nèi)某容器框架通過(guò)自定義類(lèi)加載器確保應(yīng)用中依賴(lài)的jar包不會(huì)影響到中間件運(yùn)行時(shí)使用的jar包

          • 修改類(lèi)加載方式

            類(lèi)的加載模型并非強(qiáng)制,除Bootstrap外,其他的加載并非一定要引入,或者根據(jù)實(shí)際情況在某個(gè)時(shí)間點(diǎn)進(jìn)行按需進(jìn)行動(dòng)態(tài)加載

          • 擴(kuò)展加載源

            比如從數(shù)據(jù)庫(kù)、網(wǎng)絡(luò),甚至是電視機(jī)機(jī)頂盒進(jìn)行加載

          • 防止源碼泄露

            Java代碼容易被編譯和篡改,可以進(jìn)行編譯加密。那么類(lèi)加載器也需要自定義,還原加密的字節(jié)碼。


          實(shí)現(xiàn)自定義類(lèi)加載器的步驟

          • 繼承ClassLoader

          • 重寫(xiě)findClass()方法

          • 調(diào)用defineClass()方法

          一個(gè)簡(jiǎn)單的類(lèi)加載器實(shí)現(xiàn)的示例代碼如下

          由于中間件一般都有自己的依賴(lài)jar包,在同一個(gè)工程內(nèi)引用多個(gè)框架時(shí),往往被迫進(jìn)行類(lèi)的仲裁。按某種規(guī)則jar包的版本被統(tǒng)一指定, 導(dǎo)致某些類(lèi)存在包路徑、類(lèi)名相同的情況,就會(huì)引起類(lèi)沖突,導(dǎo)致應(yīng)用程序出現(xiàn)異常。
          主流的容器類(lèi)框架都會(huì)自定義類(lèi)加載器,實(shí)現(xiàn)不同中間件之間的類(lèi)隔離,有效避免了類(lèi)沖突。


          往期推薦



          由于不知線(xiàn)程池的bug,某Java程序員叕被祭天

          程序員因重復(fù)記錄日志撐爆ELK被辭退!

          擁抱Kubernetes,再見(jiàn)了Spring Cloud

          云原生時(shí)代下的網(wǎng)關(guān)V.S反向代理




          目前交流群已有?800+人,旨在促進(jìn)技術(shù)交流,可關(guān)注公眾號(hào)添加筆者微信邀請(qǐng)進(jìn)群


          喜歡文章,點(diǎn)個(gè)“在看、點(diǎn)贊、分享”素質(zhì)三連支持一下~

          瀏覽 74
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码一区二区久久 | 成人在线免费观看视频 | 国产色小说 | 亚洲天堂人妻 | 国内精品免费视频 |