資料內(nèi)容:
加載
關(guān)于什么時(shí)候開始加載這個(gè)過程,《Java 虛擬機(jī)規(guī)范》并沒有強(qiáng)制約束,所以這一點(diǎn)我們可以
自由實(shí)現(xiàn)。加載是整個(gè)類加載過程的第一個(gè)階段,在這個(gè)階段,Java 虛擬機(jī)需要完成三件事
情:
• 通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。
• 將這個(gè)字節(jié)流表示的一種存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)換為運(yùn)行時(shí)數(shù)據(jù)區(qū)中方法區(qū)的數(shù)據(jù)結(jié)構(gòu)。
• 在內(nèi)存中生成一個(gè) Class 對(duì)象,這個(gè)對(duì)象就代表了這個(gè)數(shù)據(jù)結(jié)構(gòu)的訪問入口。
《Java 虛擬機(jī)規(guī)范》并未規(guī)定全限定名是如何獲取的,所以現(xiàn)在業(yè)界有很多獲取全限定名的方
式:
• 從 ZIP 包中讀取,最終會(huì)改變?yōu)?JAR、EAR、WAR 格式。
• 從網(wǎng)絡(luò)中獲取,最常見的應(yīng)用就是 Web Applet。
• 運(yùn)行時(shí)動(dòng)態(tài)生成,使用最多的就是動(dòng)態(tài)代理技術(shù)。
• 由其他文件生成,比如 JSP 應(yīng)用場(chǎng)景,由 JSP 文件生成對(duì)應(yīng)的 Class 文件。
• 從數(shù)據(jù)庫(kù)中讀取,這種場(chǎng)景就比較小了。
• 可以從加密文件中獲取,這是典型的防止 Class 文件被反編譯的保護(hù)措施。
加載階段既可以使用虛擬機(jī)內(nèi)置的引導(dǎo)類加載器來完成,也可以使用用戶自定義的類加載器來完
成。程序員可以通過自己定義類加載器來控制字節(jié)流的訪問方式。
數(shù)組的加載不需要通過類加載器來創(chuàng)建,它是直接在內(nèi)存中分配,但是數(shù)組的元素類型(數(shù)組去
掉所有維度的類型)最終還是要靠類加載器來完成加載。
驗(yàn)證
加載過后的下一個(gè)階段就是驗(yàn)證,因?yàn)槲覀兩弦徊街v到在內(nèi)存中生成了一個(gè) Class 對(duì)象,這個(gè)
對(duì)象是訪問其代表數(shù)據(jù)結(jié)構(gòu)的入口,所以這一步驗(yàn)證的工作就是確保 Class 文件的字節(jié)流中的
內(nèi)容符合《Java 虛擬機(jī)規(guī)范》中的要求,保證這些信息被當(dāng)作代碼運(yùn)行后,它不會(huì)威脅到虛擬
機(jī)的安全。
驗(yàn)證階段主要分為四個(gè)階段的檢驗(yàn):
• 文件格式驗(yàn)證。
• 元數(shù)據(jù)驗(yàn)證。
• 字節(jié)碼驗(yàn)證。
• 符號(hào)引用驗(yàn)證。
文件格式
文件格式驗(yàn)證
這一階段可能會(huì)包含下面這些驗(yàn)證點(diǎn):
• 魔數(shù)是否以 0xCAFEBABE 開頭。
• 主、次版本號(hào)是否在當(dāng)前 Java 虛擬機(jī)接受范圍之內(nèi)。
• 常亮池的常量中是否有不支持的常量類型。
• 指向常量的各種索引值中是否有指向不存在的常量或不符合類型的常量。
• CONSTANT_Utf8_info 型的常量中是否有不符合 UTF8 編碼的數(shù)據(jù)。
• Class 文件中各個(gè)部分及文件本身是否有被刪除的或附加的其他信息。
實(shí)際上驗(yàn)證點(diǎn)遠(yuǎn)遠(yuǎn)不止有這些,上面這些只是從 HotSpot 源碼中摘抄的一小段內(nèi)容。
元數(shù)據(jù)
元數(shù)據(jù)驗(yàn)證
這一階段主要是對(duì)字節(jié)碼描述的信息進(jìn)行語(yǔ)義分析,以確保描述的信息符合《Java 語(yǔ)言規(guī)
范》,驗(yàn)證點(diǎn)包括
• 驗(yàn)證的類是否有父類(除了 Object 類之外,所有的類都應(yīng)該有父類)。
• 要驗(yàn)證類的父類是否繼承了不允許繼承的類。
• 如果這個(gè)類不是抽象類,那么這個(gè)類是否實(shí)現(xiàn)了父類或者接口中要求的所有方法。
• 是否覆蓋了 final 字段,是否出現(xiàn)了不符合規(guī)定的重載等。
需要記住這一階段只是對(duì)《Java 語(yǔ)言規(guī)范》的驗(yàn)證。
字
字節(jié)碼驗(yàn)證
字節(jié)碼驗(yàn)證階段是最復(fù)雜的一個(gè)階段,這個(gè)階段主要是確定程序語(yǔ)意是否合法、是否是符合邏輯
的。這個(gè)階段主要是對(duì)類的方法體(Class 文件中的 Code 屬性)進(jìn)行校驗(yàn)分析。這部分驗(yàn)證包
括
• 確保操作數(shù)棧的數(shù)據(jù)類型和實(shí)際執(zhí)行時(shí)的數(shù)據(jù)類型是否一致。
• 保證任何跳轉(zhuǎn)指令不會(huì)跳出到方法體外的字節(jié)碼指令上。
• 保證方法體中的類型轉(zhuǎn)換是有效的,例如可以把一個(gè)子類對(duì)象賦值給父類數(shù)據(jù)類
型,但是不能把父類數(shù)據(jù)類型賦值給子類等諸如此不安全的類型轉(zhuǎn)換。
• 其他驗(yàn)證。
如果沒有通過字節(jié)碼驗(yàn)證,就說明驗(yàn)證出問題。但是不一定通過了字節(jié)碼驗(yàn)證,就能保證程序是
安全的。