日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

java类验证和装载顺序_Java类的加载机制和双亲委派模型

發(fā)布時間:2024/9/27 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java类验证和装载顺序_Java类的加载机制和双亲委派模型 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

Java類的加載機(jī)制和雙親委派模型

1類的加載機(jī)制

類從被加載到虛擬機(jī)內(nèi)存中開始,到卸載出內(nèi)存為止,它的整個生命周期包括了:加載(Loading)、驗證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸載(Unloading)七個階段。其中驗證、準(zhǔn)備和解析三個部分統(tǒng)稱為連接(Linking),這七個階段的發(fā)生順序如下圖所示:

如上圖所示,加載、驗證、準(zhǔn)備、初始化和卸載這五個階段的順序是確定的,類的加載過程必須按照這個順序來按部就班地開始,而解析階段則不一定,它在某些情況下可以在初始化階段后再開始。

類的生命周期的每一個階段通常都是互相交叉混合式進(jìn)行的,通常會在一個階段執(zhí)行的過程中調(diào)用或激活另外一個階段。

1.1裝載階段

在裝載階段,虛擬機(jī)主要完成三件事:

1、通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。

2、將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)域的運行時數(shù)據(jù)結(jié)構(gòu)。

3、在Java堆中生成一個代表這個類的java.lang.Class(HelloWorld.class)對象,作為方法區(qū)域數(shù)據(jù)的訪問入口

1.2驗證階段

驗證階段作用是保證Class文件的字節(jié)流包含的信息符合JVM規(guī)范,不會給JVM造成危害。如果驗證失敗,就會拋出一個java.lang.VerifyError異常或其子類異常。驗證過程分為四個階段

1、

文件格式驗證

驗證字節(jié)流文件是否符合Class文件格式的規(guī)范,并且能被當(dāng)前虛擬機(jī)正確的處理。

2、元數(shù)據(jù)驗證

是對字節(jié)碼描述的信息進(jìn)行語義分析,以保證其描述的信息符合Java語言的規(guī)范。

3、字節(jié)碼驗證

主要是進(jìn)行數(shù)據(jù)流和控制流的分析,保證被校驗類的方法在運行時不會危害虛擬機(jī)。

4、符號引用驗證

符號引用驗證發(fā)生在虛擬機(jī)將符號引用轉(zhuǎn)化為直接引用的時候,這個轉(zhuǎn)化動作將在解析階段中發(fā)生。

1.3準(zhǔn)備

準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量(static修飾的變量)初始值的階段,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配。

注釋:

1、這時候進(jìn)行內(nèi)存分配的僅包括類變量(static),而不包括實例變量,實例變量會在對象實例化時隨著對象一塊分配在Java堆中。

2、這里所設(shè)置的初始值通常情況下是數(shù)據(jù)類型默認(rèn)的零值(如0、0L、null、false等),而不是被在Java代碼中被顯式地賦予的值。

3、對已非final的變量,JVM會將其設(shè)置成“零值”,而不是其賦值語句的值:

pirvate static int size = 12;

那么在這個階段,size的值為0,而不是12。 final修飾的類變量將會賦值成真實的值。

1.4解析

解析階段是虛擬機(jī)將常量池內(nèi)的符號引用替換為直接引用的過程。

符號引用:符號引用以一組符號來描述所引用的目標(biāo),符號引用可以是任何形式的字面量,符號引用與虛擬機(jī)實現(xiàn)的內(nèi)存布局無關(guān),引用的目標(biāo)并不一定已經(jīng)在內(nèi)存中。

直接引用:直接引用可以是直接指向目標(biāo)的指針、相對偏移量或是一個能間接定位到目標(biāo)的句柄。直接引用是與虛擬機(jī)實現(xiàn)的內(nèi)存布局相關(guān)的,同一個符號引用在不同的虛擬機(jī)實例上翻譯出來的直接引用一般都不相同,如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。

1、類或接口的解析:判斷所要轉(zhuǎn)化成的直接引用是對數(shù)組類型,還是普通的對象類型的引用,從而進(jìn)行不同的解析。

2、字段解析:對字段進(jìn)行解析時,會先在本類中查找是否包含有簡單名稱和字段描述符都與目標(biāo)相匹配的字段,如果有,則查找結(jié)束;如果沒有,則會按照繼承關(guān)系從上往下遞歸搜索該類所實現(xiàn)的各個接口和它們的父接口,還沒有,則按照繼承關(guān)系從上往下遞歸搜索其父類,直至查找結(jié)束。

3、類方法解析:對類方法的解析與對字段解析的搜索步驟差不多,只是多了判斷該方法所處的是類還是接口的步驟,而且對類方法的匹配搜索,是先搜索父類,再搜索接口。

4、接口方法解析:與類方法解析步驟類似,只是接口不會有父類,因此,只遞歸向上搜索父接口就行了。

1.5初始化

類初始化階段是類加載過程的最后一步,前面的類加載過程中,除了加載(Loading)階段用戶應(yīng)用程序可以通過自定義類加載器參與之外,其余動作完全由虛擬機(jī)主導(dǎo)和控制。到了初始化階段,才真正開始執(zhí)行類中定義的Java程序代碼。

初始化階段是執(zhí)行類構(gòu)造器()方法的過程。

1、()方法是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊(static{}塊)中的語句合并產(chǎn)生的,編譯器收集的順序由語句在源文件中出現(xiàn)的順序所決定。

2、()方法與類的構(gòu)造函數(shù)不同,它不需要顯式地調(diào)用父類構(gòu)造器,虛擬機(jī)會保證在子類的()方法執(zhí)行之前,父類的()方法已經(jīng)執(zhí)行完畢,因此在虛擬機(jī)中第一個執(zhí)行的()方法的類一定是java.lang.Object。

3、由于父類的()方法先執(zhí)行,也就意味著父類中定義的靜態(tài)語句塊要優(yōu)先于子類的變量賦值操作。

4、()方法對于類或者接口來說并不是必需的,如果一個類中沒有靜態(tài)語句塊也沒有對變量的賦值操作,那么編譯器可以不為這個類生成()方法。

5、接口中可能會有變量賦值操作,因此接口也會生成()方法。但是接口與類不同,執(zhí)行接口的()方法不需要先執(zhí)行父接口的()方法。只有當(dāng)父接口中定義的變量被使用時,父接口才會被初始化。另外,接口的實現(xiàn)類在初始化時也不會執(zhí)行接口的()方法。

6、虛擬機(jī)會保證一個類的()方法在多線程環(huán)境中被正確地加鎖和同步。如果有多個線程去同時初始化一個類,那么只會有一個線程去執(zhí)行這個類的()方法,其它線程都需要阻塞等待,直到活動線程執(zhí)行()方法完畢。如果在一個類的()方法中有耗時很長的操作,那么就可能造成多個進(jìn)程阻塞。

1.6使用

Class初始化過程完后就可以被任意調(diào)用。

1.7卸載

JVM中的Class只有滿足以下三個條件,才能被GC回收,也就是該Class被卸載:

1、該類所有的實例都已經(jīng)被GC。

2、加載該類的ClassLoader實例已經(jīng)被GC。

3、該類的java.lang.Class對象沒有在任何地方被引用。

1.8研究類加載的意義

類加載是Java程序運行的第一步,研究類的加載有助于了解JVM執(zhí)行過程,并指導(dǎo)開發(fā)者采取更有效的措施配合程序執(zhí)行。

研究類加載機(jī)制的第二個目的是讓程序能動態(tài)的控制類加載,比如熱部署等,提高程序的靈活性和適應(yīng)性。

1.9加載類的開放性

類加載器(ClassLoader)是Java語言的一項創(chuàng)新,也是Java流行的一個重要原因。在類加載的第一階段“加載”過程中,需要通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流,完成這個動作的代碼塊就是類加載器。這一動作是放在Java虛擬機(jī)外部去實現(xiàn)的,以便讓應(yīng)用程序自己決定如何獲取所需的類。

虛擬機(jī)規(guī)范并沒有指明二進(jìn)制字節(jié)流要從一個Class文件獲取,或者說根本沒有指明從哪里獲取、怎樣獲取。這種開放使得Java在很多領(lǐng)域得到充分運用,例如:

1、從ZIP包中讀取,這很常見,成為JAR,EAR,WAR格式的基礎(chǔ)。

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

3、運行時計算生成,最典型的是動態(tài)代理技術(shù),在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generateProxyClass來為特定接口生成形式為“*$Proxy”的代理類的二進(jìn)制字節(jié)流。

4、有其他文件生成,最典型的JSP應(yīng)用,由JSP文件生成對應(yīng)的Class類。

1.10類加載器與類的唯一性

類加載器雖然只用于實現(xiàn)類的加載動作,但是對于任意一個類,都需要由加載它的類加載器和這個類本身共同確立其在Java虛擬機(jī)中的唯一性。通俗的說,JVM中兩個類是否“相等”,首先就必須是同一個類加載器加載的,否則,即使這兩個類來源于同一個Class文件,被同一個虛擬機(jī)加載,只要類加載器不同,那么這兩個類必定是不相等的。

這里的“相等”,包括代表類的Class對象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回結(jié)果,也包括使用instanceof關(guān)鍵字做對象所屬關(guān)系判定等情況。

2類加載的時機(jī)

2.1主動引用

一個類被主動引用之后會觸發(fā)初始化過程(加載,驗證,準(zhǔn)備需再此之前開始)。

1、遇到new、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。生成這4條指令最常見的Java代碼場景是:使用new關(guān)鍵字實例化對象時、讀取或者設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯器把結(jié)果放入常量池的靜態(tài)字段除外)時、以及調(diào)用一個類的靜態(tài)方法的時候。

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

3、當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化,則需要觸發(fā)父類的初始化。

4、當(dāng)虛擬機(jī)啟動時,用戶需要指定一個執(zhí)行的主類(包含main()方法的類),虛擬機(jī)會先初始化這個類。

5、當(dāng)使用jdk7+的動態(tài)語言支持時,如果java.lang.invoke.MethodHandle實例最后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個方法句柄所對應(yīng)的類沒有進(jìn)行過初始化,則需要先觸發(fā)器 初始化。(更多java動態(tài)類型語言資料:http://www.infoq.com/cn/articles/jdk-dynamically-typed-language)

2.2被動引用

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

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

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

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

3雙親委派模型

3.1從Java虛擬機(jī)的角度來說,只存在兩種不同的類加載器

1、啟動類加載器(Bootstrap ClassLoader),這個類加載器使用C++語言實現(xiàn)(HotSpot虛擬機(jī)中),是虛擬機(jī)自身的一部分。

2、所有其他的類加載器,這些類加載器都有Java語言實現(xiàn),獨立于虛擬機(jī)外部,并且全部繼承自java.lang.ClassLoader。

3.2從開發(fā)者的角度,類加載器可以細(xì)分為

1、啟動(Bootstrap)類加載器:負(fù)責(zé)將 Java_Home/lib下面的類庫加載到內(nèi)存中(比如rt.jar)。由于引導(dǎo)類加載器涉及到虛擬機(jī)本地實現(xiàn)細(xì)節(jié),開發(fā)者無法直接獲取到啟動類加載器的引用,所以不允許直接通過引用進(jìn)行操作。

2、標(biāo)準(zhǔn)擴(kuò)展(Extension)類加載器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實現(xiàn)的。它負(fù)責(zé)將Java_Home /lib/ext或者由系統(tǒng)變量 java.ext.dir指定位置中的類庫加載到內(nèi)存中。開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。

3、應(yīng)用程序(Application)類加載器:是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現(xiàn)的。它負(fù)責(zé)將系統(tǒng)類路徑(CLASSPATH)中指定的類庫加載到內(nèi)存中。開發(fā)者可以直接使用系統(tǒng)類加載器。由于這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,因此一般稱為系統(tǒng)(System)加載器。

4、自定義(Custom

ClassLoader)類加載器:應(yīng)用程序根據(jù)自身需要自定義的ClassLoader,如tomcat、jboss都會根據(jù)j2ee規(guī)范自行實現(xiàn)ClassLoader加載過程中會先檢查類是否被已加載,檢查順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個classloader已加載就視為已加載此類,保證此類只所有ClassLoader加載一次。而加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類。

3.3雙親委派模型

啟動、標(biāo)準(zhǔn)擴(kuò)展、應(yīng)用程序和自定義類加載器,它們之間的層次關(guān)系被稱為類加載器的雙親委派模型。該模型要求除了頂層的啟動類加載器外,其余的類加載器都應(yīng)該有自己的父類加載器,而這種父子關(guān)系一般通過組合(Composition)關(guān)系來實現(xiàn),而不是通過繼承(Inheritance)。

3.4雙親委派模型的過程

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

使用雙親委派模型的好處在于Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系。例如類java.lang.Object,它存在在rt.jar中,無論哪一個類加載器要加載這個類,最終都是委派給處于模型最頂端的Bootstrap ClassLoader進(jìn)行加載,因此Object類在程序的各種類加載器環(huán)境中都是同一個類。相反,如果沒有雙親委派模型而是由各個類加載器自行加載的話,如果用戶編寫了一個java.lang.Object的同名類并放在ClassPath中,那系統(tǒng)中將會出現(xiàn)多個不同的Object類,程序?qū)⒒靵y。因此,如果開發(fā)者嘗試編寫一個與rt.jar類庫中重名的Java類,可以正常編譯,但是永遠(yuǎn)無法被加載運行。

參考鏈接:

總結(jié)

以上是生活随笔為你收集整理的java类验证和装载顺序_Java类的加载机制和双亲委派模型的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。