做網(wǎng)站分成谷歌網(wǎng)站網(wǎng)址
一個(gè)類型從被加載到虛擬機(jī)內(nèi)存中開始,到卸載為止,它的整個(gè)生命周期將會(huì)經(jīng)過 加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用、卸載七個(gè)階段。其中 驗(yàn)證、準(zhǔn)備、解析三個(gè)部分統(tǒng)稱為 連接
1. 加載
加載是整個(gè)類加載的一個(gè)過程。在加載階段,Java虛擬機(jī)需要完成三件事情:
-
通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。
-
將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
-
在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口(生成在方法區(qū),相當(dāng)于模板)。
因?yàn)镴ava虛擬機(jī)對(duì)著三點(diǎn)要求不是特別具體,沒有指明二進(jìn)制字節(jié)流必須得從某個(gè)Class文件中獲取,確切的說根本沒有說從哪里獲取,怎么獲取,所以開發(fā)人員玩出了各種花樣。
- ?從ZIP壓縮包中讀取,這很常見,最終成為日后JAR、EAR、WAR格式的基礎(chǔ)。
- 從網(wǎng)絡(luò)中獲取,這種場(chǎng)景最典型的應(yīng)用就是WebApplet。
- 運(yùn)行時(shí)計(jì)算生成,這種場(chǎng)景使用得最多的就是動(dòng)態(tài)代理技術(shù),在javalangreect.Proxy中,就是用了ProxyGenerator.gnerateProxyClass()來為特定接口生成形式為“*SProxy”的代理類的二進(jìn)制字節(jié)流。
- 由其他文件生成,典型場(chǎng)景是JSP應(yīng)用,由ISP文件生成對(duì)應(yīng)的Class文件。
- 從數(shù)據(jù)庫中讀取,這種場(chǎng)景相對(duì)少見些,例如有些中間件服務(wù)器(如SAPNetweaver)可以選擇把程序安裝到數(shù)據(jù)庫中來完成程序代碼在集群間的分發(fā)。
- 可以從加密文件中獲取,這是典型的防Class文件被反編譯的保護(hù)措施,通過加載時(shí)解密Class文件來保障程序運(yùn)行邏輯不被窺探。?
數(shù)組類
對(duì)于數(shù)組類而言,情況就有所不同,數(shù)組類本身不通過類加載器創(chuàng)建,它是由Java虛擬機(jī)直接在內(nèi)存中動(dòng)態(tài)構(gòu)造出來的。但數(shù)組類與類加載器仍然有很密切的關(guān)系,因?yàn)閿?shù)組類的元素類型 (ElementType,指的是數(shù)組去掉所有維度的類型) 最終還是要靠類加載器來完成加載,一個(gè)數(shù)組類(下面簡(jiǎn)稱為C)創(chuàng)建過程遵循以下規(guī)則:
- ?如果數(shù)組的組件類型(Componcnt Typc,指的是數(shù)組去掉一個(gè)維度的類型,注意和前面的元素類型區(qū)分開米)是引用類型,那就遞歸采用本節(jié)中定義的加載過程去加載這個(gè)組件類型,數(shù)組C將被標(biāo)識(shí)在加載該組件類型的類加載器的類名稱空間上(這點(diǎn)很重要,在7.4節(jié)會(huì)介紹,一個(gè)類型必須與類加載器一起確定唯一性)。
- 如果數(shù)組的組件類型不是引用類型(例如int]數(shù)組的組件類型為int),Java虛擬機(jī)將會(huì)把數(shù)組C標(biāo)記為與引導(dǎo)類加載器關(guān)聯(lián)。
- 數(shù)組類的可訪問性與它的組件類型的可訪問性一致,如果組件類型不是引用類型,它的數(shù)組類的可訪問性將默認(rèn)為public,可被所有的類和接口訪問到。
總結(jié)
加載階段與連接階段的部分動(dòng)作(如一部分字節(jié)碼文件格式驗(yàn)證動(dòng)作)是交叉進(jìn)行的,加載階段尚未完成,連接階段可能已經(jīng)開始,但這些夾在加載階段之中進(jìn)行的動(dòng)作,仍然屬于連接階段的一部分,這兩個(gè)階段的開始時(shí)間仍然保持著固定的先后順序。
2. 驗(yàn)證
目的是確保Class文件的字節(jié)流包含的信息符和《Java虛擬機(jī)規(guī)范》的全部約束要求,保證信息被當(dāng)作代碼運(yùn)行后不會(huì)危害虛擬機(jī)自身安全。
驗(yàn)證過程:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證。
2.1. 文件格式驗(yàn)證
第一階段要驗(yàn)證字節(jié)流是否符合Class文件格式的規(guī)范,并且能被當(dāng)前版本的虛擬機(jī)處理。
2.2. 元數(shù)據(jù)驗(yàn)證
元數(shù)據(jù):元數(shù)據(jù)是指用來描述數(shù)據(jù)的數(shù)據(jù),更通俗一點(diǎn),就是描述代碼間關(guān)系,或者代碼與其他資源(例如數(shù)據(jù)庫表)之間內(nèi)在聯(lián)系的數(shù)據(jù)。
第一, 元數(shù)據(jù)以標(biāo)簽的形式存在于Java代碼中。 ? 第二, 元數(shù)據(jù)描述的信息是類型安全的,即元數(shù)據(jù)內(nèi)部的字段都是有明確類型的。 ? 第三, 元數(shù)據(jù)需要編譯器之外的工具額外的處理用來生成其它的程序部件。 ? 第四, 元數(shù)據(jù)可以只存在于Java源代碼級(jí)別,也可以存在于編譯之后的Class文件內(nèi)部。
JDK5.0出來后,java語言中就有了四種類型(TYPE),即類(class)、枚舉(enum)、接口(interface)和注解(@interface),它們是處在同一級(jí)別的。java就是通過注解來表示元數(shù)據(jù)的。
主要目的是對(duì)類的元數(shù)據(jù)信息進(jìn)行語義校驗(yàn),保證不存在與《Java語言規(guī)范》定義相勃的元數(shù)據(jù)信息。
總結(jié)
驗(yàn)證階段對(duì)于虛擬機(jī)的類加載機(jī)制來說,是一個(gè)非常重要的、但卻不是必須要執(zhí)行的階段,因?yàn)轵?yàn)證階段只有通過或者不通過的差別,只要通過了驗(yàn)證,其后就對(duì)程序運(yùn)行期沒有任何影響了。如果程序運(yùn)行的全部代碼(包括自己編寫的、第三方包中的、從外部加載的、動(dòng)態(tài)生成的等所有代碼)都已經(jīng)被反復(fù)使用和驗(yàn)證過,在生產(chǎn)環(huán)境的實(shí)施階段就可以考慮使用-Xverify: none參 數(shù)來關(guān)閉大部分的類驗(yàn)證措施,以縮短虛擬機(jī)類加載的時(shí)間。
3. 準(zhǔn)備
準(zhǔn)備階段是正式為類中定義(即靜態(tài)變量,被static修飾的變量)分配內(nèi)存并設(shè)置類變量初始值的階段。
4. 解析
解析階段是Java虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過程
符號(hào)引用和直接引用
符號(hào)引用:
-
符號(hào)引用以一組符號(hào)來描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用時(shí)能無歧義地定位到目標(biāo)即可。
直接引用:
-
直接引用是可以直接指向目標(biāo)的指針、相對(duì)偏移量或者是一個(gè)能間接定位到目標(biāo)的句柄。
解析動(dòng)作主要針對(duì):類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點(diǎn)限定符這7類符號(hào)引用進(jìn)行。
5. 初始化
直到初始化階段,Java虛擬機(jī)才真正開始執(zhí)行類中編寫的Java程序代碼,將主導(dǎo)權(quán)移交給應(yīng)用程序。
初始化含義:根據(jù)程序員通過程序編碼制定的主觀計(jì)劃去初始化類變量和其他資源。也可以理解初始化階段就是執(zhí)行類構(gòu)造器<clinit>()方法的過程。