深入理解Java虛擬機(八)—— 類加載原理

一、Class類文件的結(jié)構

????????任何一個Class文件都對應著唯一一個類或接口的定義信息,但是反過來不成立,類或接口并不一定都得定義在文件里(比如類或接口也可以動態(tài)生成,直接送入類加載器中)。

? ? ? ? Class文件是一組以8個字節(jié)為基礎單位的二進制流,各數(shù)據(jù)項按順序緊湊的排列,中間沒有添加任何分隔符,因此Class文件中存儲的內(nèi)容幾乎全是程序運行的必要數(shù)據(jù),沒有空隙存在,當遇到需要占用8字節(jié)以上空間的數(shù)據(jù)項時,按照高位在前的方式分成若干8字節(jié)進行存儲。

? ? ? ? Class文件格式采用類似于C語言結(jié)構來存儲數(shù)據(jù),包含兩種數(shù)據(jù)類型:

? ? ? ? (1)無符號數(shù):屬于基本的數(shù)據(jù)類型。u1、u2、u8分別代表1個字節(jié)、2個字節(jié)、8個字節(jié)的無符號數(shù),無符號數(shù)可以用來描述數(shù)字、索引引用、數(shù)量值或按照UTF-8編碼構成的字符串值。

? ? ? ? (2):表是有多個無符號數(shù)或者其他表作為數(shù)據(jù)項構成的復合數(shù)據(jù)類型,所有表的命名都習慣以“_info”結(jié)尾。整個Class文件本質(zhì)上也可以看做一張表,由以下數(shù)據(jù)項按照嚴格順序排列構成:

圖 1-1 Class文件數(shù)據(jù)項

二、類生命周期

? ? ? ? 虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進行校驗、轉(zhuǎn)換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這個過程被稱為虛擬機的類加載機制。一個類型從被加載到虛擬內(nèi)存中開始,到卸載出內(nèi)存為止,整個生命周期會經(jīng)歷七個階段:

圖 2-1 類的生命周期

? ? ? ? 其中,加載、驗證、準備、初始化、卸載這五個階段的順序是肯定的,類加載過程必須按照這種順序進行。而解析則不一定,在某些情況下,解析可以再初始化階段之后開始,即動態(tài)綁定晚期綁定

? ? ? ? 加載過程中的第一階段,加載,由虛擬機自行把握時機開始。但對于“初始化”,有且只有以下六種情況必須立即對類進行“初始化”:

? ? ? ? (1)遇到new、getstatic、putstatic、invokestatic字節(jié)碼指令時,如果類型沒有初始化,則需要觸發(fā)初始化;

????????能夠生成上述四條字節(jié)碼指令的場景由:(1)使用new關鍵字實例化對象的時候 (2)讀取或設置一個類型的靜態(tài)字段時 (3)嗲用一個類型的靜態(tài)方法時 。

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

? ? ? ? (3)當初始化類的時候,如果發(fā)現(xiàn)父類還沒有初始化,則需要先觸發(fā)父類的初始化;

? ? ? ? (4)當虛擬機啟動時,需要指定一個主類(main()),虛擬機會先初始化這個主類;

? ? ? ? (5)如果一個java.lang.invoke.MethodHandle實例最后解析結(jié)果為REF_getStatic、REF_putStatic、REF_invokeStatic、REF_newInvokeSpecial四種類型的方法句柄,并且句柄對應的類沒有初始化過,則需要先觸發(fā)初始化;

? ? ? ? (6)當一個接口定義了默認方法(default關鍵字修飾的接口方法),如果該接口的實現(xiàn)類發(fā)生了初始化,則該接口要在其之前被初始化。

? ? ? ? 這六種場景,也稱為對一個類型的主動引用。除此之外的所有引用都不會觸發(fā)初始化,被稱為被動引用

三、類加載過程

? ? ? ? 虛擬機中,類加載過程分為:加載、驗證、準備、解析、初始化五個階段。

圖 3-1 類加載過程

1. 加載

? ? ? ? 虛擬機在加載過程中,主要完成三件事:

? ? ? ? (1)通過一個類的全限定名來獲取定義此類的二進制字節(jié)流;

? ? ? ? (2)將字節(jié)流代表的靜態(tài)存儲結(jié)構轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構;

? ? ? ? (3)在內(nèi)存中生成一個代表該類的java.lang.Class對象,作為方法區(qū)該類的數(shù)據(jù)訪問入口。

? ? ? ? 加載階段結(jié)束后,虛擬機外部的二進制字節(jié)流就按照虛擬機設定的格式存儲在方法區(qū)中,并在堆中實例化一個Class類的對象作為程序訪問方法區(qū)的外部接口。

2. 驗證

? ? ? ? 這一階段是確保Class文件的字節(jié)流中包含的信息符合虛擬機規(guī)范的全部約束,確保這些信息不會危害虛擬機的安全。

? ? ? ? 加載過程中,并沒有指明字節(jié)流的來源必須是Class文件,因此可以通過鍵盤輸入等形式產(chǎn)生惡意的字節(jié)流文件,因此驗證是必須的,如果不驗證檢查輸入的字節(jié)流,很可能造成惡意字節(jié)碼流導致的系統(tǒng)崩潰。

????????驗證階段主要包含這四個階段的檢驗:文件格式檢驗、元數(shù)據(jù)檢驗、字節(jié)碼檢驗、符號引用檢驗

? ? (1)文件格式檢驗:是否符合Class文件格式的規(guī)范;

? ? (2)元數(shù)據(jù)檢驗:對字節(jié)碼描述信息進行語義分析,比如檢驗當前類是否有父類、當前類的父類是否繼承了不允許被繼承的類等;

? ? (3)字節(jié)碼驗證:通過數(shù)據(jù)流分析和控制流分析,檢驗程序語義的合法性和符合邏輯;

? ? (4)符號引用驗證:發(fā)生在虛擬機將符號引用轉(zhuǎn)化為直接引用的時候,對類自身以外的各類信息進行匹配性校驗,校驗該類是否缺少或者被進制訪問它依賴的某些外部類、方法、字段得資源,如果檢驗符號引用中通過字符串描述的全限定名是否能找到對應的類、符號引用中的類和字段能否被當前類訪問等。

驗證階段是一個重要,但非必須的階段,如果在測試環(huán)境反復執(zhí)行且沒有問題的項目,可以考慮在生產(chǎn)環(huán)境使用 -X verify:none 來設置關閉大部分的類驗證,來縮短虛擬機類加載時間。

3. 準備

? ? ? ? 準備階段是正式為類中定義的變量分配內(nèi)存并設置初始值的階段,這里的初始值通常情況是賦0值的階段。該階段進行內(nèi)存分配的僅包含類變量,而不包含實例變量。

public static int? a = 123 ;

????????在準備階段過后,a的初始值為0,而不是123,因為此時尚未執(zhí)行Java方法,而把a的值賦為123的指令時程序被編譯后。

? ? ? ? 除了通常情況外,還有一類特殊情況:

publid static final int a = 123;

? ? ? ? 編譯時Javac將會為a生成ConstantValue屬性,在準備階段直接根據(jù)ConstantValue將a的值設置為123。

4. 解析

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

????????符號引用:用一組符號來描述引用的目標,符號可以是任何形式的字面量,只有使用時能無歧義的定位到目標即可。符號引用的字面量形式明確定義在Class文件格式中。

? ? ? ? 直接引用:直接引用是可以指向目標的指針、相對偏移量或者是能間接定位到目標的句柄。

5. 初始化

? ? ? ? 直到最后一個階段初始化,虛擬機才真正開始執(zhí)行Java程序代碼,將主導權交給應用程序。在準備階段,變量已經(jīng)賦過一次初始0值,而在初始化階段,則會根據(jù)程序編碼制定的主觀計劃去初始化類變量和其他資源。

四、類加載器

? ? ? ? 把類加載階段“通過類的全限定名獲取描述該類的二進制字節(jié)流”的動作放在虛擬機外部實現(xiàn),以便讓程序自己決定如何去獲取所需的類,實現(xiàn)這個動作的代碼被稱為“類加載器”(Class Loader)。

? ? ? ? 對于每一個類,都需要由它的類加載器和這個類本身一起共同確立其在虛擬機中的唯一性,每一個類加載器,都擁有一個獨立的類名稱空間。

創(chuàng)建類加載器的語法結(jié)構為:

ClassLoader myClassLoader = new ClassLoader() {??

? ??@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {

? ??????????return ;

? ? }

}

? ? ? ? 如果兩個類由不同的類加載器加載,即便兩個類來源于同一個Class文件,只要類加載器不同,這兩個類必定不相等。示例代碼:

圖 4-1 示例代碼

? ? ? ? 運行結(jié)果:

圖 4-2 運行結(jié)果
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容