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

          面試必問:JVM的類加載機制

          共 3476字,需瀏覽 7分鐘

           ·

          2020-12-16 18:22

          1 類加載過程

          想要使用一個類,首先需要將其加載到JVM中,類加載到JVM需要經(jīng)過三個步驟:加載->鏈接->初始化。其中鏈接又分為驗證,準備,解析三步。
          cf273f1d427e5a4c2577b89583c24dfd.webp

          1.1 加載

          類加載階段會在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法這個類的各個數(shù)據(jù)的入口。注意相關(guān)信息不是一定要從Class文件中獲取,它即可從ZIP中讀取(如從Jar包,War包中讀取),也可以在運算時生成(動態(tài)代理),也可以由其它文件生成(如將JSP文件轉(zhuǎn)換成對應(yīng)的Class類)。

          1.2 鏈接

          鏈接過程分為驗證,準備,解析三步。

          1.2.1 驗證

          JAVA是一種相對安全的語言,驗證的意義是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機要求。不會危害到虛擬機的安全。驗證主要包含文件格式驗證,元數(shù)據(jù)驗證,字節(jié)碼驗證,符號引用驗證。

          • 文件格式驗證:驗證字節(jié)流是否符合Class文件格式規(guī)范,并且能被當(dāng)前JVM加載處理。如常量類型是否支持。

          • 元數(shù)據(jù)驗證:字節(jié)碼信息進行語言分析,分析是否符合java語言規(guī)范。

          • 字節(jié)碼驗證:最重要的驗證環(huán)節(jié),元數(shù)據(jù)驗證后對方法體驗證,保證類方法在運行時不會危害發(fā)生。

          • 符號引用驗證:驗證符號引用,保證能夠訪問到,不會出現(xiàn)無法訪問的情況。

          1.2.2 準備

          準備階段正式對類變量分配內(nèi)存,并設(shè)置初始默認值。如定義 private static int s = 2020;經(jīng)過此階段s=0,而不是2020.但是如果定義為private final static int s = 2020,在準備階段會直接將s設(shè)置成2020而不是初始值0;

          1.2.3 解析

          解析階段JVM將常量池中的符號引用替換為直接引用。準備階段只是分配了內(nèi)存,但是類變量并沒有指向那一塊內(nèi)存,這一步就是完成實際指向的工作。

          1.3 初始化

          初始化階段為類變量設(shè)置正確的初始值。如上文 private static int s = 2020中將s賦值2020的動作便在這一步完成。同時初始化階段也會執(zhí)行靜態(tài)代碼塊。如果有超類,則先對超類執(zhí)行初始化。
          初始化階段是執(zhí)行類構(gòu)造器方法的過程,方法是編譯器自動收集類中的類變量賦值操作和靜態(tài)語句塊中的語句合并而成的。JVM會保證父類的方法會在子類的方法執(zhí)行之前執(zhí)行完畢。如果這個類沒有靜態(tài)變量賦值和靜態(tài)代碼塊,那么編譯器可以不為這個類生成方法。
          以下幾種情況不會觸發(fā)初始化:

          • 子類引用父類的靜態(tài)變量,只會觸發(fā)父類的初始化,不會觸發(fā)子類的初始化

          • 定義對象數(shù)組,不會觸發(fā)初始化

          • 常量在編譯過程中直接存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用定義常量的類,不會觸發(fā)定義常量所在的類的初始化。如A引用B類的常量final static x = 3。此時不會對B進行初始化。

          • 通過類名獲取Class對象,不會觸發(fā)類的初始化。

          • 通過Class.forName加載指定類時,如果initalize為false,不會觸發(fā)初始化。

          • 通過ClassLoader默認的loadClss方法,也不會觸發(fā)初始化。

          2 類加載器

          類加載的動作由類加載器完成。對于JVM來說,類的唯一性是通過類的全限定名+類加載器來區(qū)分的。不同的類加載器加載的同一個類并不被認為是同一類。如有一個類C,分別用類加載器CL1,CL2加載。一個參數(shù)需要CL1的C實例,傳入的確CL2的C實例,就會報錯java.lang.ClassCastException:C cannot be cast to C。
          JVM提供了三種類加載器:啟動類加載器(Bootstrap ClassLoader),擴展類加載器(Extension ClassLoader),應(yīng)用程序類加載器(Application ClassLoader).

          • 啟動類加載器:用來加載Java的核心庫,主要加載的是JVM自身所需要的類,使用C++實現(xiàn),并非繼承于java.lang.ClassLoader,是JVM的一部分。負責(zé)加載JAVA_HOME\lib目錄中的,或者-Xbootclasspath參數(shù)指定的路徑中的,且被虛擬機認可[注1]的類。開發(fā)者無法直接獲取到其引用。
            注1:JVM是按文件名識別的,如rt.jar,如果文件名不被虛擬機識別,即使把jar包放在lib目錄下也沒有作用,同時啟動加載器只加載包名為java,javax,sun等開頭的類。且java是特殊包名,開發(fā)者的包名不能以java開頭,如果自定義了一個java.***包來讓類加載器加載,那么就會拋出異常java.lang.SecurityException: Prohibited package name: java.***

          • 擴展類加載器:用來加載Java的擴展庫。負責(zé)加載JAVA_HOME\lib\ext目錄中的,或通過系統(tǒng)變量java.ext.dirs指定路徑中的類庫。由java語言實現(xiàn)。開發(fā)者可以直接使用。

          • 應(yīng)用程序類加載器:負責(zé)加載用戶路徑(classpath)上的類庫。開發(fā)者可以直接使用。可以通過ClassLoader.getSystemClassLoader()獲得。一般情況下程序的默認類加載器就是該加載器。

          除了提供的加載器外,開發(fā)者可以通過繼承ClassLoader類的方式實現(xiàn)自己的類加載器。
          37b3642bcc9db9c4c8092d73721b3bfa.webp

          3 雙親委派機制

          JVM的類加載機制為雙親委派機制,除了頂層的啟動類加載器,雙親委派機制要求每一個類加載器都要有自己的父加載器。這個父加載器并不是指繼承,而是一種委派關(guān)系。雙親委派機制下一個類加載器收到類加載請求不會直接自己去加載,而是先把這個請求委托給自己的父類加載器去執(zhí)行,如果父類加載器還存在父類加載器,則繼續(xù)委托,一直委托到最頂層的啟動類加載器。父加載器可以加載目標類的話就由父加載器完成加載任務(wù),如果父加載器無法完成則子加載器自己嘗試加載。這就是雙親委派機制。
          雙親委派機制的優(yōu)勢:雙親委派機制使得java類隨著他的類加載器具備了一種帶有帶有優(yōu)先級的層級關(guān)系。通過這種層級關(guān)系可以避免類的的重復(fù)加載,當(dāng)父加載器已經(jīng)加載過目標類時,子加載器無需重復(fù)加載一次。
          其次是安全方面,通過雙親委派機制可以避免核心類不會被隨意替換,例如網(wǎng)絡(luò)傳遞一個名為java.lang.Integer的類,在雙親委派機制下,加載請求會被傳遞到頂層的啟動類加載器,啟動類加載器發(fā)現(xiàn)這個名字的類已經(jīng)加載過了,就會直接返回已經(jīng)加載過的Integer.class。這樣就可以防止核心API被修改。

          4 OSGI

          OSGI(Open Service Gateway Initiative)是面向java的動態(tài)模型系統(tǒng)。OSGI能夠提供無需重啟的動態(tài)改造功能,基于OSGI的程序很可能可以實現(xiàn)模塊級的熱插拔功能,當(dāng)程序進行升級時,只需停用,重新安裝然后啟動程序的一部分。但并非所有的程序都適合OSGI架構(gòu),其在提供強大功能的同時也提高了復(fù)雜度,因為OSGI不支持雙親委派機制。
          eclipse就是基于OSGI技術(shù)來構(gòu)建的。
          OSGI每個模塊都自己的類,每個模塊可以聲明其需要模塊的類(導(dǎo)入),也可以聲明自己的類以供其它模塊使用(導(dǎo)出),每個模塊都有自己的類加載器,他負責(zé)加載本模塊的類,對于非本模塊的類:核心庫的類會代理給父類加載器(通常是啟動類加載器),其他模塊導(dǎo)入的類則代理給對應(yīng)模塊由其加載。對于java開頭的類,默認都是由父類加載器完成的,可以通過org.osgi.framework.bootdelegation設(shè)置某些包或者類必須由父類加載器加載。如設(shè)置org.osgi.framework.bootdelegation = com.my.* 此時com.my下的所有類都由父類加載器進行加載
          例如由兩個模塊:M-A和M-B,分別有類C-A和C-B,C-A繼承自C-B,M-A啟動時,對C-A進行加載,因為繼承關(guān)系繼而需要加載C-B,此時由于M-A聲明了C-B是由M-B導(dǎo)入的,那么就會將C-B的加載交由M-B執(zhí)行,M-B對C-B進行加載,所得的類實例可以被所有聲明導(dǎo)入此類的模塊使用。

          本文鏈接:https://blog.csdn.net/yue_hu/article/details/109622483


          end


          *版權(quán)聲明:轉(zhuǎn)載文章和圖片均來自公開網(wǎng)絡(luò),版權(quán)歸作者本人所有,推送文章除非無法確認,我們都會注明作者和來源。如果出處有誤或侵犯到原作者權(quán)益,請與我們聯(lián)系刪除或授權(quán)事宜。


          長按識別圖中二維碼

          關(guān)注獲取更多資訊




          不點關(guān)注,我們哪來故事?



          d577509098724ab31839506bf458adf7.webp

          點個再看,你最好看




          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  中国夫妻操逼视频 | 影音先锋毛片 | 亚洲天堂有码无码视频 | 一级中国毛片 | 五月骚婷婷 |