摘要:驗(yàn)證驗(yàn)證階段的主要目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。不同的虛擬機(jī)對類驗(yàn)證的實(shí)現(xiàn)可能會(huì)有所不同,但大致都會(huì)完成以下四個(gè)階段的驗(yàn)證文件格式的驗(yàn)證元數(shù)據(jù)的驗(yàn)證字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。
原文地址
虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗(yàn),轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機(jī)直接使用的Java類型,This
is the class loading mechanism of the virtual machine
本文基于HotSpot虛擬機(jī)
類加載類從被加載到虛擬機(jī)內(nèi)存開始,到卸載出內(nèi)存為止,整個(gè)過程包括加載(Loading)、驗(yàn)證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸載(Unloading)7個(gè)階段。其中驗(yàn)證、準(zhǔn)備、解析3部分統(tǒng)稱為連接。
其中類加載的過程包括了加載、驗(yàn)證、準(zhǔn)備、解析、初始化五個(gè)階段。在這五個(gè)階段中,加載、驗(yàn)證、準(zhǔn)備和初始化這四個(gè)階段發(fā)生的順序是確定的,而解析階段則不一定,它在某些情況下可以在初始化階段之后開始,這是為了支持Java語言的運(yùn)行時(shí)綁定(也成為動(dòng)態(tài)綁定或晚期綁定)。另外注意這里的幾個(gè)階段是按順序開始,而不是按順序進(jìn)行或完成,因?yàn)檫@些階段通常都是互相交叉地混合進(jìn)行的,通常在一個(gè)階段執(zhí)行的過程中調(diào)用或激活另一個(gè)階段。
關(guān)于靜態(tài)綁定和動(dòng)態(tài)綁定:
靜態(tài)綁定(前期綁定)是指:在程序運(yùn)行前就已經(jīng)知道方法是屬于那個(gè)類的,在編譯的時(shí)候就可以連接到類的中,定位到這個(gè)方法。
在Java中,final、private、static修飾的方法以及構(gòu)造函數(shù)都是靜態(tài)綁定的,不需程序運(yùn)行,不需具體的實(shí)例對象就可以知道這個(gè)方法的具體內(nèi)容。
動(dòng)態(tài)綁定(后期綁定)是指:在程序運(yùn)行過程中,根據(jù)具體的實(shí)例對象才能具體確定是哪個(gè)方法。
動(dòng)態(tài)綁定是多態(tài)性得以實(shí)現(xiàn)的重要因素,它通過方法表來實(shí)現(xiàn):每個(gè)類被加載到虛擬機(jī)時(shí),在方法區(qū)保存元數(shù)據(jù),其中,包括一個(gè)叫做 方法表(method table)的東西,表中記錄了這個(gè)類定義的方法的指針,每個(gè)表項(xiàng)指向一個(gè)具體的方法代碼。如果這個(gè)類重寫了父類中的某個(gè)方法,則對應(yīng)表項(xiàng)指向新的代碼實(shí)現(xiàn)處。從父類繼承來的方法位于子類定義的方法的前面。
類加載的過程 加載加載是“類加載”過程的一個(gè)階段,這個(gè)階段需要完成以下3件事情:
通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流
將這個(gè)字節(jié)流所代表的靜態(tài)儲(chǔ)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu) (將類信息、靜態(tài)變量、字節(jié)碼、常量這些.class文件中的內(nèi)容放入方法區(qū)中)
在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口
關(guān)于獲取類的二進(jìn)制字節(jié)流的方法,虛擬機(jī)并沒有指明要從哪里獲取,如何獲取。
在Java的發(fā)展歷程中,主要出現(xiàn)了以下幾種方法
從ZIP包中讀取
從網(wǎng)絡(luò)獲取(例如:Applet)
運(yùn)行時(shí)計(jì)算生成(例如:動(dòng)態(tài)代理)
有其他文件生成(例如:JSP應(yīng)用)
從數(shù)據(jù)庫中讀取
相對于類加載的其他階段而言,加載階段(準(zhǔn)確地說,是加載階段獲取類的二進(jìn)制字節(jié)流的動(dòng)作)是可控性最強(qiáng)的階段,因?yàn)殚_發(fā)人員既可以使用系統(tǒng)提供的類加載器來完成加載,也可以自定義自己的類加載器來完成加載。
加載階段完成后,虛擬機(jī)外部的二進(jìn)制字節(jié)流就按照虛擬機(jī)所需的格式存儲(chǔ)在方法區(qū)之中,而且在Java堆中也創(chuàng)建一個(gè)java.lang.Class類的對象,這樣便可以通過該對象訪問方法區(qū)中的這些數(shù)據(jù)。
驗(yàn)證驗(yàn)證階段的主要目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。
危險(xiǎn)因素的來源:
Java語言本身是安全的,但是由于Class文件并不一定由Java源代碼編譯而來。所以很可能會(huì)載入有害的字節(jié)流而導(dǎo)致系統(tǒng)崩潰。
不同的虛擬機(jī)對類驗(yàn)證的實(shí)現(xiàn)可能會(huì)有所不同,但大致都會(huì)完成以下四個(gè)階段的驗(yàn)證:文件格式的驗(yàn)證、元數(shù)據(jù)的驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。
文件格式驗(yàn)證主要驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能夠被當(dāng)前版本的虛擬機(jī)處理。主要包括以下這些驗(yàn)證點(diǎn):
是否以魔數(shù)0xCAFEBABE開頭
主,次版本號(hào)是否在當(dāng)前虛擬機(jī)處理范圍之內(nèi)
常量池的常量中是否有不被支持的常量類型(檢查常量tag標(biāo)志)
指向常量的各種索引值中是否有指向不存在的常量或不符合類型的常量。
CONSTANT_Utf8_info型的常量中是否有不符合UTF8編碼的數(shù)據(jù)
Class文件中各個(gè)部分及文件本身是否有被刪除或附加的其他信息
魔數(shù)的概念:很多類型的文件,其起始的幾個(gè)字節(jié)的內(nèi)容是固定的(或是有意填充,或是本就如此)。根據(jù)這幾個(gè)字節(jié)的內(nèi)容就可以確定文件類型,因此這幾個(gè)字節(jié)的內(nèi)容被稱為魔數(shù) (magic number)。
class文件魔數(shù)CAFEBABE的由來
這個(gè)階段的驗(yàn)證是基于二進(jìn)制字節(jié)流進(jìn)行的,之后的3個(gè)驗(yàn)證階段全部基于方法區(qū)的存儲(chǔ)結(jié)構(gòu)進(jìn)行的,不會(huì)在直接操作字節(jié)流。
元數(shù)據(jù)驗(yàn)證對字節(jié)碼描述的信息進(jìn)行語義分析(其實(shí)就是對類中的各數(shù)據(jù)類型進(jìn)行語法校驗(yàn)),以保證其描述的信息符合Java語言的規(guī)范要求。
這個(gè)類是否有父類
這個(gè)類的父類是否繼承了不允許被繼承的類
如果這個(gè)類不是抽象類,是否實(shí)現(xiàn)了其父類或接口之中要求實(shí)現(xiàn)的所有方法。
類中的字段,方法是否與父類長生矛盾
。。。
字節(jié)碼驗(yàn)證該階段驗(yàn)證的主要工作是進(jìn)行數(shù)據(jù)流和控制流分析,對類的方法體進(jìn)行校驗(yàn)分析,以保證被校驗(yàn)的類的方法在運(yùn)行時(shí)不會(huì)做出危害虛擬機(jī)安全的行為。
符號(hào)引用驗(yàn)證這是最后一個(gè)階段的驗(yàn)證,它發(fā)生在虛擬機(jī)將符號(hào)引用轉(zhuǎn)化為直接引用的時(shí)候(解析階段中發(fā)生該轉(zhuǎn)化,后面會(huì)有講解),主要是對類自身以外的信息(常量池中的各種符號(hào)引用)進(jìn)行匹配性的校驗(yàn)。通常要校驗(yàn)以下內(nèi)容:
符號(hào)引用中通過字符串描述的全限定名是否能找到對應(yīng)的類
在指定類中是否存在符合方法的字段描述符以及簡單名稱所描述的方法和字段
符合引用中的類,字段,方法的訪問性是否可以被當(dāng)前類訪問
準(zhǔn)備準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。這時(shí)候進(jìn)行內(nèi)存分配的僅包括類變量(被static修飾的變量),而不包括實(shí)例變量,實(shí)例變量將會(huì)在對象實(shí)例化時(shí)隨著對象一起分配在堆中。其次,這里所說的初始值“通常情況”下是數(shù)據(jù)類型的零值
假設(shè)一個(gè)類變量的定義為:
public static int value = 10;
那么變量value在準(zhǔn)備階段過后的初始值為0,而不是10,因?yàn)檫@時(shí)候尚未開始執(zhí)行任何Java方法,而把value賦值為3的putstatic指令是在程序編譯后,存放于類構(gòu)造器
基本數(shù)據(jù)類型的零值如下:
這一階段還需要注意如下幾點(diǎn):
對基本數(shù)據(jù)類型來說,對于類變量(static)和全局變量,如果不顯式地對其賦值而直接使用,則系統(tǒng)會(huì)為其賦予默認(rèn)的零值,而對于局部變量來說,在使用前必須顯式地為其賦值,否則編譯時(shí)不通過。
對于同時(shí)被static和final修飾的常量,必須在聲明的時(shí)候就為其顯式地賦值,否則編譯時(shí)不通過;而只被final修飾的常量則既可以在聲明時(shí)顯式地為其賦值,也可以在類初始化時(shí)顯式地為其賦值,總之,在使用前必須為其顯式地賦值,系統(tǒng)不會(huì)為其賦予默認(rèn)零值。
對于引用數(shù)據(jù)類型reference來說,如數(shù)組引用、對象引用等,如果沒有對其進(jìn)行顯式地賦值而直接使用,系統(tǒng)都會(huì)為其賦予默認(rèn)的零值,即null。
如果在數(shù)組初始化時(shí)沒有對數(shù)組中的各元素賦值,那么其中的元素將根據(jù)對應(yīng)的數(shù)據(jù)類型而被賦予默認(rèn)的零值。
如果類字段的字段屬性表中存在ConstantValue屬性,即同時(shí)被final和static修飾,那么在準(zhǔn)備階段變量value就會(huì)被初始化為ConstValue屬性所指定的值。
假設(shè)上面的類變量value被定義為:
public static final int value = 10;
編譯時(shí)Javac將會(huì)為value生成ConstantValue屬性,在準(zhǔn)備階段虛擬機(jī)就會(huì)根據(jù)ConstantValue的設(shè)置將value賦值為10。
static final常量在編譯期就將其結(jié)果放入了調(diào)用它的類的常量池中。
例如:
public class Test { public static int value = 10; public Test() { System.out.println("This is Test Class"); } } public class Main { public static void main(String[] args) { System.out.println(Test.value); } }
以上代碼只會(huì)打印10,不會(huì)打印This is Test Class
解析解析階段是虛擬機(jī)將常量池中的符號(hào)引用轉(zhuǎn)化為直接引用的過程。
符號(hào)引用,以一組符號(hào)來描述所引用的目標(biāo),符號(hào)可以是任意形式的字面量,只要使用時(shí)可以無歧義的定位到目標(biāo)即可。與虛擬機(jī)的內(nèi)存布局無關(guān),引用的目標(biāo)不一定已經(jīng)加載到內(nèi)存中
直接引用,可以是直接指向目標(biāo)的指針,相對偏移量或是一個(gè)能間接定位到目標(biāo)的句柄。與虛擬機(jī)的內(nèi)存布局有關(guān),同一個(gè)符號(hào)引用在不同虛擬機(jī)實(shí)例上翻譯出來的直接引用一般不相同。如果有直接引用,那么引用的目標(biāo)必定在內(nèi)存中存在。
對同一個(gè)符號(hào)引用進(jìn)行多次解析請求時(shí)很常見的事情,虛擬機(jī)實(shí)現(xiàn)可能會(huì)對第一次解析的結(jié)果進(jìn)行緩存(在運(yùn)行時(shí)常量池中記錄直接引用,并把常量標(biāo)示為已解析狀態(tài)),從而避免解析動(dòng)作重復(fù)進(jìn)行。
解析動(dòng)作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符7類符號(hào)引用進(jìn)行。
類和接口的解析判斷所要轉(zhuǎn)化成的直接引用是對數(shù)組類型,還是普通的對象類型的引用,從而進(jìn)行不同的解析。
字段解析對字段進(jìn)行解析時(shí),會(huì)先在本類中查找是否包含有簡單名稱和字段描述符都與目標(biāo)相匹配的字段,如果有,則查找結(jié)束;如果沒有,則會(huì)按照繼承關(guān)系從上往下遞歸搜索該類所實(shí)現(xiàn)的各個(gè)接口和它們的父接口,還沒有,則按照繼承關(guān)系從上往下遞歸搜索其父類,直至查找結(jié)束。
class Super{ public static int m = 11; static{ System.out.println("執(zhí)行了super類靜態(tài)語句塊"); } } class Father extends Super{ public static int m = 33; static{ System.out.println("執(zhí)行了父類靜態(tài)語句塊"); } } class Child extends Father{ static{ System.out.println("執(zhí)行了子類靜態(tài)語句塊"); } } public class StaticTest{ public static void main(String[] args){ System.out.println(Child.m); } } 執(zhí)行結(jié)果如下: 執(zhí)行了super類靜態(tài)語句塊 執(zhí)行了父類靜態(tài)語句塊 33 如果注釋掉Father類中對m定義的那一行,則輸出結(jié)果如下: 執(zhí)行了super類靜態(tài)語句塊 11
static變量發(fā)生在靜態(tài)解析階段,也即是初始化之前,此時(shí)已經(jīng)將字段的符號(hào)引用轉(zhuǎn)化為了內(nèi)存引用,也便將它與對應(yīng)的類關(guān)聯(lián)在了一起,由于在子類中沒有查找到與m相匹配的字段,那么m便不會(huì)與子類關(guān)聯(lián)在一起,因此并不會(huì)觸發(fā)子類的初始化。
理論上是按照上述順序進(jìn)行搜索解析,但在實(shí)際應(yīng)用中,虛擬機(jī)的編譯器實(shí)現(xiàn)可能要比上述規(guī)范要求的更嚴(yán)格一些。如果有一個(gè)同名字段同時(shí)出現(xiàn)在該類的接口和父類中,或同時(shí)在自己或父類的接口中出現(xiàn),編譯器可能會(huì)拒絕編譯。
類方法解析:對類方法的解析與對字段解析的搜索步驟差不多,只是多了判斷該方法所處的是類還是接口的步驟,而且對類方法的匹配搜索,是先搜索父類,再搜索接口。
接口方法解析:與類方法解析步驟類似,知識(shí)接口不會(huì)有父類,因此,只遞歸向上搜索父接口就行了。
初始化初始化階段是類加載過程的最后一步,初始化階段是真正執(zhí)行類中定義的Java程序代碼(或者說是字節(jié)碼)的過程。初始化過程是一個(gè)執(zhí)行類構(gòu)造器
Java虛擬機(jī)規(guī)范嚴(yán)格規(guī)定了有且只有5種場景必須立即對類進(jìn)行初始化:
使用new關(guān)鍵字實(shí)例化對象、讀取或者設(shè)置一個(gè)類的靜態(tài)字段(被final修飾的靜態(tài)字段除外)、調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候
使用java.lang.reflect包中的方法對類進(jìn)行反射調(diào)用的時(shí)候
初始化一個(gè)類,發(fā)現(xiàn)其父類還沒有初始化過的時(shí)候
虛擬機(jī)啟動(dòng)的時(shí)候,虛擬機(jī)會(huì)先初始化用戶指定的包含main()方法的那個(gè)類
當(dāng)使用jdk1.7動(dòng)態(tài)語言支持時(shí),如果一個(gè)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且這個(gè)方法句柄所對應(yīng)的類沒有進(jìn)行初始化,則需要先出觸發(fā)其初始化。(沒理解是什么意思)
class Father{ public static int a = 1; static{ a = 2; } } class Child extends Father{ public static int b = a; } public class ClinitTest{ public static void main(String[] args){ System.out.println(Child.b); } }
執(zhí)行上面的代碼,會(huì)打印出2,也就是說b的值被賦為了2。
我們來看得到該結(jié)果的步驟。首先在準(zhǔn)備階段為類變量分配內(nèi)存并設(shè)置類變量初始值,這樣A和B均被賦值為默認(rèn)值0,而后再在調(diào)用
如果我們顛倒一下Father類中“public static int a = 1;”語句和“static語句塊”的順序,程序執(zhí)行后,則會(huì)打印出1。很明顯是根據(jù)規(guī)則1,執(zhí)行Father的
另外,在顛倒二者的順序之后,如果在static語句塊中對a進(jìn)行訪問(比如將a賦給某個(gè)變量),在編譯時(shí)將會(huì)報(bào)錯(cuò),因?yàn)楦鶕?jù)規(guī)則1,它只能對a進(jìn)行賦值,而不能訪問。
類加載器類加載器雖然只用于實(shí)現(xiàn)類的加載動(dòng)作,但它在Java程序中起到的作用卻遠(yuǎn)遠(yuǎn)不限于類的加載階段。對于任意一個(gè)類,都需要由它的類加載器和這個(gè)類本身一同確定其在就Java虛擬機(jī)中的唯一性,也就是說,即使兩個(gè)類來源于同一個(gè)Class文件,只要加載它們的類加載器不同,那這兩個(gè)類就必定不相等。這里的“相等”包括了代表類的Class對象的equals()、isAssignableFrom()、isInstance()等方法的返回結(jié)果,也包括了使用instanceof關(guān)鍵字對對象所屬關(guān)系的判定結(jié)果。
從Java虛擬機(jī)的角度來講,只存在兩種不同的類加載器:
啟動(dòng)類加載器:它使用C++實(shí)現(xiàn)(這里僅限于Hotspot,也就是JDK1.5之后默認(rèn)的虛擬機(jī),有很多其他的虛擬機(jī)是用Java語言實(shí)現(xiàn)的),是虛擬機(jī)自身的一部分。
所有其他的類加載器:這些類加載器都由Java語言實(shí)現(xiàn),獨(dú)立于虛擬機(jī)之外,并且全部繼承自抽象類java.lang.ClassLoader,這些類加載器需要由啟動(dòng)類加載器加載到內(nèi)存中之后才能去加載其他的類。
從Java開發(fā)人員的角度來看,類加載器可以大致劃分為以下三類:
啟動(dòng)類加載器:Bootstrap ClassLoader,跟上面相同。它負(fù)責(zé)加載存放在JDKjrelib(JDK代表JDK的安裝目錄,下同)下,或被-Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類庫(如rt.jar,所有的java.*開頭的類均被Bootstrap ClassLoader加載)。啟動(dòng)類加載器是無法被Java程序直接引用的。
擴(kuò)展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載JDKjrelibext目錄中,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器。
應(yīng)用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn),它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類,開發(fā)者可以直接使用該類加載器,如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器。
應(yīng)用程序都是由這三種類加載器互相配合進(jìn)行加載的,如果有必要,我們還可以加入自定義的類加載器。因?yàn)镴VM自帶的ClassLoader只是懂得從本地文件系統(tǒng)加載標(biāo)準(zhǔn)的java class文件,因此如果編寫了自己的ClassLoader,便可以做到如下幾點(diǎn):
在執(zhí)行非置信代碼之前,自動(dòng)驗(yàn)證數(shù)字簽名。
動(dòng)態(tài)地創(chuàng)建符合用戶特定需要的定制化構(gòu)建類。
從特定的場所取得java class,例如數(shù)據(jù)庫中和網(wǎng)絡(luò)中。
事實(shí)上當(dāng)使用Applet的時(shí)候,就用到了特定的ClassLoader,因?yàn)檫@時(shí)需要從網(wǎng)絡(luò)上加載java class,并且要檢查相關(guān)的安全信息,應(yīng)用服務(wù)器也大都使用了自定義的ClassLoader技術(shù)。
如上圖展示的類加載之間的這種層次關(guān)系,稱為類加載器的雙親委派模型 我們把每一層上面的類加載器叫做當(dāng)前層類加載器的父加載器,當(dāng)然,它們之間的父子關(guān)系并不是通過繼承關(guān)系來實(shí)現(xiàn)的,而是使用組合關(guān)系來復(fù)用父加載器中的代碼。該模型在JDK1.2期間被引入并廣泛應(yīng)用于之后幾乎所有的Java程序中,但它并不是一個(gè)強(qiáng)制性的約束模型,而是Java設(shè)計(jì)者們推薦給開發(fā)者的一種類的加載器實(shí)現(xiàn)方式。
雙親委派模型的工作流程是:如果一個(gè)類加載器收到了類加載的請求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把請求委托給父加載器去完成,依次向上,因此,所有的類加載請求最終都應(yīng)該被傳遞到頂層的啟動(dòng)類加載器中,只有當(dāng)父加載器在它的搜索范圍中沒有找到所需的類時(shí),即無法完成該加載,子加載器才會(huì)嘗試自己去加載該類。
使用雙親委派模型來組織類加載器之間的關(guān)系,有一個(gè)很明顯的好處,就是Java類隨著它的類加載器(說白了,就是它所在的目錄)一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系,這對于保證Java程序的穩(wěn)定運(yùn)作很重要。例如,類java.lang.Object類存放在JDKjrelib下的rt.jar之中,因此無論是哪個(gè)類加載器要加載此類,最終都會(huì)委派給啟動(dòng)類加載器進(jìn)行加載,這邊保證了Object類在程序中的各種類加載器中都是同一個(gè)類。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/71110.html
摘要:加載階段在類的加載階段,虛擬機(jī)需要完成以下件事情通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。驗(yàn)證階段驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。 注:本篇文章中的內(nèi)容是根據(jù)《深入理解Java虛擬機(jī)--JVM高級(jí)特性與最佳實(shí)踐》而總結(jié)的,如有理解錯(cuò)誤,歡迎大家指正! 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件...
摘要:實(shí)現(xiàn)這個(gè)口號(hào)的就是可以運(yùn)行在不同平臺(tái)上的虛擬機(jī)和與平臺(tái)無關(guān)的字節(jié)碼。類加載過程加載加載是類加載的第一個(gè)階段,虛擬機(jī)要完成以下三個(gè)過程通過類的全限定名獲取定義此類的二進(jìn)制字節(jié)流。驗(yàn)證目的是確保文件字節(jié)流信息符合虛擬機(jī)的要求。 引言 我們知道java代碼編譯后生成的是字節(jié)碼,那虛擬機(jī)是如何加載這些class字節(jié)碼文件的呢?加載之后又是如何進(jìn)行方法調(diào)用的呢? 一 類文件結(jié)構(gòu) 無關(guān)性基石 ja...
摘要:最終形成可以被虛擬機(jī)最直接使用的類型的過程就是虛擬機(jī)的類加載機(jī)制。即重寫一個(gè)類加載器的方法驗(yàn)證驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。 《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(第二版》讀書筆記與常見相關(guān)面試題總結(jié) 本節(jié)常見面試題(推薦帶著問題閱讀,問題答案在文中都有提到): 簡單說說類加載過...
摘要:二驗(yàn)證驗(yàn)證主要是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)的自身安全。五初始化類的初始化階段是類加載過程的最后一步,該階段才真正開始執(zhí)行類中定義的程序代碼或者說是字節(jié)碼。 關(guān)注我,每天三分鐘,帶你輕松掌握一個(gè)Java相關(guān)知識(shí)點(diǎn)。 虛擬機(jī)(JVM)經(jīng)常出現(xiàn)在我們面試中,但是工作中卻很少遇到,導(dǎo)致很多同學(xué)沒有去了解過。其實(shí)除了應(yīng)付面試,作為java程序員,了解...
摘要:虛擬機(jī)為了保證一個(gè)類的方法在多線程環(huán)境中被正確地加鎖同步。但啟動(dòng)類加載器不可能認(rèn)識(shí)這些代碼。實(shí)現(xiàn)模塊化熱部署的關(guān)鍵則是它的自定義類加載器機(jī)制的實(shí)現(xiàn)。 概念區(qū)分:加載、類加載、類加載器 類加載是一個(gè)過程。 加載(Loading)是類加載這一個(gè)過程的階段。 類加載器是ClassLoader類或其子類。 本文中的類的描述都包括了類和接口的可能性,因?yàn)槊總€(gè)Class文件都有可能代表J...
閱讀 3554·2021-11-24 11:17
閱讀 2325·2021-11-15 11:38
閱讀 3401·2021-10-14 09:42
閱讀 2968·2019-08-30 15:54
閱讀 2053·2019-08-28 18:09
閱讀 568·2019-08-26 11:48
閱讀 1655·2019-08-26 10:48
閱讀 2175·2019-08-26 10:45