類加載過(guò)程

-
加載
在加載階段,Java虛擬機(jī)需要完成的三件事情:
????1、通過(guò)類的全限定名來(lái)獲取類的二進(jìn)制字節(jié)流。
????2、將字節(jié)流的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
????3、在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這些數(shù)據(jù)的訪問(wèn)入口。
類加載.jpg 驗(yàn)證
確保Class文件的字節(jié)流中包含的信息符合《Java虛擬機(jī)的規(guī)范》的全部約束要求,保證這些信息被當(dāng)作代碼運(yùn)行后不會(huì)危害虛擬機(jī)的自身的安全。
????1、文件格式驗(yàn)證
????2、元數(shù)據(jù)驗(yàn)證
????3、字節(jié)碼驗(yàn)證
????4、符號(hào)引用驗(yàn)證準(zhǔn)備
為類中定義的變量(即靜態(tài)變量,被static修飾的變量)分配內(nèi)存并設(shè)置類變量初始值。解析
Java虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程。
解析動(dòng)作主要針對(duì)類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符這7類符號(hào)引用進(jìn)行,分別對(duì)應(yīng)于常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dynamic_info和CONSTANT_InvokeDynamic_info8種常量類型。
有且僅有六種情況對(duì)類進(jìn)行初始化
1、遇到new、getstatic、putstatic或invokestatic這四條字節(jié)碼指令時(shí),如果類型沒(méi)有進(jìn)行初始化,則需要先觸發(fā)其初始化階段。能生成四條指令的場(chǎng)景有:
????1.1、使用new關(guān)鍵字實(shí)例化對(duì)象
????1.2、讀取或設(shè)置一個(gè)類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時(shí)候。
????1.3、調(diào)用一個(gè)類的靜態(tài)方法的時(shí)候。
2、使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候,如果類沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。
3、當(dāng)初始化類的時(shí)候,如果發(fā)現(xiàn)其父類沒(méi)有進(jìn)行初始化,則需要先觸發(fā)其父類的初始化。
4、當(dāng)虛擬機(jī)啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(包含main()方法的那個(gè)類),虛擬機(jī)先初始化這個(gè)主類。
5、當(dāng)java.lang.invoke.MethodHandle實(shí)例解析結(jié)果為REF_getstatic、REF_putstatic、REF_invokestatic、REF_newinvokeSpecial四種類型的方法句柄,并且這個(gè)方法句柄對(duì)應(yīng)的類沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。
6、當(dāng)接口中定義了JDK 8新加入的默認(rèn)方法(被default關(guān)鍵字修飾的接口方法)時(shí),如果這個(gè)接口的實(shí)現(xiàn)類發(fā)生了初始化,那接口要在其之前被初始化。
以上6種場(chǎng)景是主動(dòng)引用,當(dāng)被動(dòng)引用時(shí),則不會(huì)觸發(fā)初始化:
1、通過(guò)子類引用父類的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化而不會(huì)觸發(fā)子類的初始化。
2、通過(guò)數(shù)組定義引用類,不會(huì)觸發(fā)此類的初始化,數(shù)組直接繼承java.lang.Obejct,創(chuàng)建動(dòng)作由字節(jié)碼指令newarry觸發(fā)。
3、常量在編譯階段會(huì)存入調(diào)用類的常量池中,本質(zhì)上沒(méi)有直接引用到定義常量的類,因此不會(huì)觸發(fā)定義常量的類的初始化。
類加載器

雙親委派
雙親委派模型的工作過(guò)程是:如果一個(gè)類加載器收到類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把請(qǐng)求委派給父類加載器去完成,每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到最頂層的啟動(dòng)類加載器中,只有當(dāng)父類加載器反饋?zhàn)约簾o(wú)法完成這個(gè)加載請(qǐng)求(它搜索的范圍中沒(méi)有找到所需的類)時(shí),子加載器才會(huì)嘗試去完成加載。
為什么要搞雙親委派?
主要出于安全考慮,可以保證類只會(huì)被加載一次,避免重復(fù)加載。
ClassLoader源碼解析

findLoadedClass -> parent.loadClass -> findClass
當(dāng)該類沒(méi)有被自己加載過(guò)時(shí),就調(diào)用父加載器的loadClass方法。如果父類加載器沒(méi)有加載到該類,就使用自己的findClass方法查找該類進(jìn)行加載。如果沒(méi)有找到這個(gè)類則會(huì)拋出ClassNotFoundException異常。
