溫馨提示×

溫馨提示×

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

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

java中類加載機制和類加載器的區(qū)別是什么

發(fā)布時間:2021-02-20 16:10:46 來源:億速云 閱讀:466 作者:Leah 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細講解有關(guān)java中類加載機制和類加載器的區(qū)別是什么,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

類加載機制

java類從被加載到JVM到卸載出JVM,整個生命周期包括:加載(Loading)、驗證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸載(Unloading)七個階段。

其中驗證、準(zhǔn)備和解析三個部分統(tǒng)稱為連接(Linking)。

java中類加載機制和類加載器的區(qū)別是什么

1、加載

加載指的是將類的.class文件中的二進制數(shù)據(jù)讀入到內(nèi)存中,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在堆區(qū)創(chuàng)建一個java.lang.Class對象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)。

java中類加載機制和類加載器的區(qū)別是什么

類的加載通過JVM提供的類加載器完成,類加載器是程序運行的基礎(chǔ)。程序在啟動的時候,并不會一次性加載程序所要用到的所有class文件,而是根據(jù)需要,通過java的類加載器機制(classLoader)來動態(tài)加載某個class文件到內(nèi)存中。

jvm在運行時會產(chǎn)生三個classLoader:

啟動類加載器(BootStrap ClassLoader):是java類加載層次中最頂層的類加載器,負責(zé)加載jdk中的核心類庫。由C++實現(xiàn),不是classLoader的子類。

擴展類加載器(Extension ClassLoader):負責(zé)加載java的擴展類庫,比如lib/ext或者java.ext.dirs系統(tǒng)屬性指定的目錄中的jar包。父類加載器為null。

系統(tǒng)類加載器(App ClassLoader):負責(zé)加載來自java命令的-classpath選項、java.class.path系統(tǒng)屬性所指定的jar包和類路徑。程序可以通過classLoader的靜態(tài)方法getSystemClassLoader(),來獲取系統(tǒng)類加載器。由java語言實現(xiàn),父類加載器為ExtClassLoader。

除了java默認提供的這三個classLoader之外,用戶可以根據(jù)需要定義自己的classLoader,這些自定義的classLoader都必須繼承自java.lang.ClassLoader類。

通過使用不同的類加載器,可以從不同來源加載類的二進制數(shù)據(jù)。通常有如下幾種情況:

從本地文件系統(tǒng)加載class文件,這是絕大部分實例程序的類加載方式。從jar包加載class類,這種方式也很常見。通過網(wǎng)絡(luò)加載class類把一個java源文件動態(tài)編譯,并執(zhí)行加載,比如jsp。

2、連接

當(dāng)類被加載之后,系統(tǒng)為之生成一個對應(yīng)的class對象,接著進入連接階段(驗證-準(zhǔn)備-解析),連接階段負責(zé)把類的二進制數(shù)據(jù)合并到j(luò)re中。

驗證:用于檢測被加載的類是否有正確的內(nèi)部結(jié)構(gòu),并和其他類協(xié)調(diào)一致。

包括四種驗證:文件格式驗證、元數(shù)據(jù)驗證、字節(jié)驗證和符號引用驗證。準(zhǔn)備:負責(zé)為類變量分配內(nèi)存,并設(shè)置默認初始值。

解析:將類的二進制數(shù)據(jù)中的變量進行符號引用替換成直接引用。

3、初始化

在初始化階段,主要為類的靜態(tài)變量賦予正確的初始值。其實就是執(zhí)行類構(gòu)造器<clinit>()方法的過程。

在java類中對類變量指定初始值有兩種方式:a.聲明類變量時指定初始值;b.使用靜態(tài)初始化塊為類變量指定初始值。

jvm初始化一個類包含如下步驟:

加載并連接該類先初始化其直接父類依次執(zhí)行初始化語句當(dāng)執(zhí)行第2步時,系統(tǒng)對直接父類的初始化也遵循1~3,以此類推。

當(dāng)一個類被主動引用后會觸發(fā)初始化過程:

遇到new、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。

生成這4條指令最常見的Java代碼場景是:使用new關(guān)鍵字實例化對象時、讀取或者設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯器把結(jié)果放入常量池的靜態(tài)字段除外)時、以及調(diào)用一個類的靜態(tài)方法的時候。

使用java.lang.reflect包的方法對類進行反射調(diào)用的時候,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。

當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進行過初始化,則需要觸發(fā)父類的初始化。當(dāng)虛擬機啟動時,用戶需要指定一個執(zhí)行的主類(包含main()方法的類),虛擬機會先初始化這個類。

當(dāng)使用jdk7+的動態(tài)語言支持時,如果java.lang.invoke.MethodHandle實例最后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個方法句柄所對應(yīng)的類沒有進行過初始化,則需要先觸發(fā)器 初始化。

當(dāng)一個類如果是被動引用的話,不會觸發(fā)初始化過程:

通過子類引用父類的靜態(tài)字段,不會導(dǎo)致子類初始化。對于靜態(tài)字段,只有直接定義該字段的類才會被初始化,因此當(dāng)我們通過子類來引用父類中定義的靜態(tài)字段時,只會觸發(fā)父類的初始化,而不會觸發(fā)子類的初始化。

通過數(shù)組定義來引用類,不會觸發(fā)此類的初始化。

常量在編譯階段會存入調(diào)用類的常量池中,本質(zhì)上沒有直接引用到定義常量的類,因此不會觸發(fā)定義常量的類的初始化。

4、使用

(略)

5、卸載

如果出現(xiàn)下面的情況,類就會被卸載:

該類所有的實例都已經(jīng)被回收,也就是java堆中不存在該類的任何實例。加載該類的ClassLoader已經(jīng)被回收。

該類對應(yīng)的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。

如果以上三個條件全部滿足,jvm就會在方法區(qū)垃圾回收的時候?qū)︻愡M行卸載,類的卸載過程其實就是在方法區(qū)中清空類信息,java類的整個生命周期就結(jié)束了。

類加載器

類加載器負責(zé)加載所有的類。其為所有被載入內(nèi)存中的類生成一個java.lang.Class實例對象。一旦一個類被加載如JVM中,同一個類就不會被再次載入了。

正如一個對象有一個唯一的標(biāo)識一樣,一個載入JVM的類也有一個唯一的標(biāo)識。

在Java中,一個類用其全限定類名(包括包名和類名)作為標(biāo)識;但在JVM中,一個類用其全限定類名和其類加載器作為其唯一標(biāo)識。

例如,如果在pg的包中有一個名為Person的類,被類加載器ClassLoader的實例kl負責(zé)加載,則該Person類對應(yīng)的Class對象在JVM中表示為(Person.pg.kl)。

這意味著兩個類加載器加載的同名類:(Person.pg.kl)和(Person.pg.kl2)是不同的、它們所加載的類也是完全不同、互不兼容的。

前面我們已經(jīng)介紹了java中的幾種類加載器,下面我們用一張圖展示他們的層次關(guān)系:

java中類加載機制和類加載器的區(qū)別是什么

類加載步驟

類加載器加載class大致需要如下8個步驟:

檢測此Class是否載入過,即在緩沖區(qū)中是否有此Class,如果有直接進入第8步,否則進入第2步。

如果沒有父類加載器,則要么Parent是根類加載器,要么本身就是根類加載器,則跳到第4步,如果父類加載器存在,則進入第3步。

請求使用父類加載器去載入目標(biāo)類,如果載入成功則跳至第8步,否則接著執(zhí)行第5步。

請求使用根類加載器去載入目標(biāo)類,如果載入成功則跳至第8步,否則跳至第7步。

當(dāng)前類加載器嘗試尋找Class文件,如果找到則執(zhí)行第6步,如果找不到則執(zhí)行第7步。

從文件中載入Class,成功后跳至第8步。

拋出ClassNotFountException異常。返回對應(yīng)的java.lang.Class對象。

類加載機制

全盤負責(zé):當(dāng)一個類加載器負責(zé)加載某個Class時,該Class所依賴和引用其他Class也將由該類加載器負責(zé)載入,除非顯示使用另外一個類加載器來載入。

雙親委派:先讓父類加載器試圖加載該Class,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類。

通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務(wù)委托給父加載器,依次遞歸,如果父加載器可以完成類加載任務(wù),就成功返回;只有父加載器無法完成此加載任務(wù)時,才自己去加載。

緩存機制:保證所有加載過的Class都會被緩存,當(dāng)程序中需要使用某個Class時,類加載器先從緩存區(qū)中搜尋該Class,只有當(dāng)緩存區(qū)中不存在該Class對象時,系統(tǒng)才會讀取該類對應(yīng)的二進制數(shù)據(jù),并將其轉(zhuǎn)換成Class對象,存入緩沖區(qū)中。

這就是為很么修改了Class后,必須重新啟動JVM,程序所做的修改才會生效的原因。

自定義的類加載器

jvm除跟類加載器之外的所有類加載器都是ClassLoader子類的實例,開發(fā)者可以通過拓展ClassLoader的子類,并重寫該ClassLoader所包含的方法實現(xiàn)自定義的類加載器。

ClassLoader有如下兩個關(guān)鍵方法:

loadClass(String name,boolean resolve):該方法為ClassLoader的入口點,根據(jù)指定名稱來加載類,系統(tǒng)就是調(diào)用ClassLoader的該方法來獲取指定類的class對象。

findClass(String name):根據(jù)指定名稱來查找類如果需要實現(xiàn)自定義的ClassLoader,則可以通過重寫以上兩個方法來實現(xiàn),通常推薦重寫findClass()方法而不是loadClass()方法。

classLoader()方法的執(zhí)行步驟:

1)findLoadedClass():來檢查是否加載類,如果加載直接返回;

2)父類加載器上調(diào)用loadClass()方法。如果父類加載器為null,則使用跟類加載器加載;

3)調(diào)用findClass(String)方法查找類。從這邊可以看出,重寫findClass()方法可以避免覆蓋默認類加載器的父類委托,緩沖機制兩種策略;如果重寫loadClass()方法,則實現(xiàn)邏輯更為復(fù)雜。

關(guān)于java中類加載機制和類加載器的區(qū)別是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI