虛擬機(jī)如何加載Class文件蓖租?
Class文件中的信息進(jìn)入到虛擬機(jī)后會發(fā)生什么變化粱侣?
虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗(yàn)蓖宦、轉(zhuǎn)換解析和初始化齐婴,最終形成可以被虛擬機(jī)直接使用的Java類型,這就是虛擬機(jī)的類加載機(jī)制稠茂。
何時(shí)加載?
- 遇到new柠偶、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時(shí),如果類沒有進(jìn)行過初始化诱担,則需要先觸發(fā)初始化毡证。
場景:new 實(shí)例化對象時(shí)候、讀取或設(shè)置一個(gè)類的靜態(tài)字段的時(shí)候蔫仙,以及調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候料睛。
- 遇到new柠偶、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時(shí),如果類沒有進(jìn)行過初始化诱担,則需要先觸發(fā)初始化毡证。
- 2)使用java.lang.reflect包的方法對類進(jìn)行反射調(diào)用的時(shí)候,如果類沒有進(jìn)行過初始化匀哄,則需要先觸發(fā)期初始化秦效。
- 3)當(dāng)初始化一個(gè)類的時(shí)候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化涎嚼,則需要先觸發(fā)其父類的初始化阱州。
- 4)當(dāng)虛擬機(jī)啟動時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(包含main()方法的那個(gè)類)法梯,虛擬機(jī)會先初始化這個(gè)主類苔货。
- 5)當(dāng)使用JDK1.7的動態(tài)語言支持的,如果一個(gè)java.lang.invoke.MethodHandle實(shí)例最后的解析結(jié)果REF_getStatic立哑、REF_putStatic夜惭、REF_invokeStatic的方法句柄,并且這個(gè)方法句柄所對應(yīng)的類沒有進(jìn)行初始化铛绰,則需要先觸發(fā)其初始化诈茧。
類加載過程
1.加載
在該階段,虛擬機(jī)需要完成以下3件事情:
- 1)通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流
- 2)將這個(gè)字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)捂掰。
- 3)在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對象敢会,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口。
獲取類的二進(jìn)制字節(jié)流是開發(fā)人員可控性最強(qiáng)的这嚣,開發(fā)人員可以定義自己的類加載器去控制字節(jié)流的獲取方式(重寫loadClass()方法)鸥昏。
數(shù)組類加載 - 1)如果數(shù)組的組件類型是引用類型,則遞歸加載這個(gè)組件類型姐帚,數(shù)組將在加載該組件類型的類加載器的類名稱空間上被標(biāo)識
- 2)如果數(shù)組的組件類型不是引用類型(如int[]數(shù)組)吏垮,Java虛擬機(jī)將會把數(shù)組標(biāo)記與引導(dǎo)類加載器關(guān)聯(lián)。
- 3)數(shù)組類的可見性與它的組件類型的可見性一致罐旗,如果組件類型不是引用類型膳汪,那數(shù)組類的可見性將默認(rèn)為public。
2.驗(yàn)證
驗(yàn)證是連接階段的第一步九秀,這階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求遗嗽,并且不會危害虛擬機(jī)自身的安全。
- 1)文件格式驗(yàn)證
- 2)元數(shù)據(jù)驗(yàn)證
- 3)字節(jié)碼驗(yàn)證
- 4)符號引用驗(yàn)證
3.準(zhǔn)備
準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段颤霎,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。
數(shù)據(jù)類型 | 零值 |
---|---|
int | 0 |
long | 0L |
short | (short)0 |
char | '\u0000' |
byte | (byte)0 |
boolean | false |
float | 0.0f |
double | 0.0d |
reference | null |
4.解析
解析階段是虛擬機(jī)將常量池內(nèi)的符號引用替換為直接引用的過程。
- 符號引用:以一組符號來描述所引用的目標(biāo)友酱,符號可以是任何形式的字面量晴音,只要使用時(shí)能無歧義地定位到目標(biāo)即可。
- 直接引用:可以是直接指向目標(biāo)的指針缔杉、相對偏移量或是一個(gè)能間接定位到目標(biāo)的句柄锤躁。
- 1)類或接口的解析
- 2)字段解析
- 3)類方法解析
- 4)接口方法解析
5初始化
初始化階段是執(zhí)行類構(gòu)造器<clinit>()方法的過程。
類加載器
類加載器實(shí)現(xiàn)的是類加載過程中的第一步“通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流”或详。類加載器在類的層次劃分系羞、OSGi、熱部署霸琴、代碼加密等領(lǐng)域大放異彩椒振。
對于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立其在Java虛擬機(jī)中的唯一性梧乘,每一個(gè)類加載器澎迎,都擁有一個(gè)獨(dú)立的類名稱空間。
1.雙親委派模型
- 1)啟動類加載器(Bootstrap ClassLoader)一般加載類庫
- 2)擴(kuò)展類加載器(Extension ClassLoader)一般加載擴(kuò)展庫
- 3)應(yīng)用程序類加載器(Application ClassLoader)
父子之間的關(guān)系一般不會用繼承的關(guān)系來實(shí)現(xiàn)选调,而是都使用組合關(guān)系來復(fù)用父加載器的代碼夹供。
雙親委派模型的工作過程是:如果一個(gè)類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個(gè)類仁堪,而是把這個(gè)請求委派給父類加載器去完成哮洽,每一層次的類加載器都是如此,因此所有的加載請求最終都應(yīng)該傳遞到頂層的啟動類加載器中弦聂,只有當(dāng)父加載器反饋?zhàn)约簾o法完成這個(gè)請求時(shí)鸟辅,子加載器才會去嘗試自己去加載。
2.破壞雙親委派模型
OSGi中對類加載器的使用時(shí)很值得學(xué)習(xí)的横浑,弄懂了OSGi的實(shí)現(xiàn)剔桨,就可以算是掌握了類加載的精髓。