java 類加載過程

1. 為什么使用類加載器.
類加載時在運(yùn)行期完成的,所以類加載的時候,會增加性能開銷,但是會提高java的靈活度. eg.面向接
口的應(yīng)用程序,等到運(yùn)行的時候在指定實(shí)現(xiàn)類.

2.類加載過程

  2.1 類加載定義
jvm把編譯后的.Class字節(jié)碼文件加載到內(nèi)存中,對數(shù)據(jù)進(jìn)行校驗(yàn)--驗(yàn)證解析--初始化形成可以被虛擬
機(jī)可以直接使用的java類型的過程,就是虛擬機(jī)的類加載機(jī)制.

  2.2 類加載過程
類從被加載到虛擬機(jī)內(nèi)存中開始,到卸載出內(nèi)存為止,生命周期包括:加載,驗(yàn)證,準(zhǔn)備,解析,初始化,使
用,卸載這幾個階段.
其中加載(裝載),驗(yàn)證,準(zhǔn)備,初始化和卸載這5個階段的順序是固定的,但是解析階段卻是不一定的,在
某些情況下會在初始化之后在解析,(這是由于運(yùn)行時動態(tài)綁定的特性,在調(diào)用接口的時候,只有在運(yùn)行時
才知道具體的實(shí)現(xiàn)類).

1. 加載階段,加載階段也叫作裝載階段,主要完成以下工作.
  1.1   通過全類名獲取定義此類的二進(jìn)制字節(jié)流
  1.2   將字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)的運(yùn)行時數(shù)據(jù)結(jié)構(gòu)
  1.3   在堆中生成一個代表該類的Class對象,作為方法區(qū)這些數(shù)據(jù)的訪問入口
說明:在整個類加載的過程中(具體是獲取類二進(jìn)制字節(jié)流的階段),加載階段是可控性最強(qiáng)的階段,是因
為加載階段可以使用系統(tǒng)通過的類加載器來完成,也可以使用用戶自定義的類加載器來完成,開發(fā)人員可
通過類加載器控制字節(jié)流的獲取方式.

2. 驗(yàn)證階段,這個階段主要是確保class文件中的字節(jié)流符合當(dāng)前虛擬機(jī)的要求,不危害虛擬機(jī).主要包
括以下幾個過程:
  2.1   文件格式驗(yàn)證, 驗(yàn)證class文件的格式規(guī)范. eg.class文件是不是已魔數(shù)開頭,版本號等.
  2.2   元數(shù)據(jù)驗(yàn)證,這個階段是對字節(jié)碼描述信息進(jìn)行語義分析,保證信息符合java的語言規(guī)范要求. 
eg.該類是不是有Object的父類,是不是繼承了final的父類,是否實(shí)現(xiàn)了接口中的方法等等
  2.3   字節(jié)碼校驗(yàn),這個階段主要是對類中的方法體進(jìn)行驗(yàn)證,保證該類中的方法在運(yùn)行時不會發(fā)生危
害虛擬機(jī)行為. eg,父類可以指向子類,但是不能子類指向父類,保證跳轉(zhuǎn)指令不會跳到方法體以外的地方.
  2.4   符號引用驗(yàn)證,這個階段主要通過字符串描述的全限定名是否能找到對應(yīng)的類,以及方法字段的
訪問控制,(private、protected、public、default)是否能被訪問.

3. 準(zhǔn)備階段.這個階段是正式為變量分配內(nèi)存并且設(shè)置變量初始值的階段,這些內(nèi)存都在方法區(qū)進(jìn)行分配.
這塊有需要注意的地方:此時的內(nèi)存分配僅僅包括static變量,而不包括實(shí)例變量(實(shí)例變量會隨著對象
的創(chuàng)建在java堆中分配),這時候的初始值一般情況下都是0.或者false,例如,有變量public static 
int value = 100;那value在準(zhǔn)備階段之后的值是0而不是100,把value賦值為100是在之后的初始化
階段.當(dāng)然這個也不是絕對的,例如常量 public static final int value = 100,因?yàn)榇藭rvalue
是常量,虛擬機(jī)在準(zhǔn)備階段的時候就會設(shè)置為100.

4. 解析階段,將常量池中的符號引用轉(zhuǎn)換為直接用的過程.
  1.符號引用:符號引用是一組符號來描述所引用的目標(biāo)對象,符號可以是任何形式的字面量,只要使用
時能無歧義地定位到目標(biāo)即可。符號引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無關(guān),引用的目標(biāo)對象并不一定已經(jīng)
加載到內(nèi)存中。
  2.直接引用:直接引用可以是直接指向目標(biāo)對象的指針、相對偏移量或是一個能間接定位到目標(biāo)的句柄。
直接引用是與虛擬機(jī)內(nèi)存布局實(shí)現(xiàn)相關(guān)的,同一個符號引用在不同虛擬機(jī)實(shí)例上翻譯出來的直接引用一
般不會相同,如果有了直接引用,那引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。

5.初始化階段,類的初始化是類加載的最后一步,該過程是根據(jù)程序員主觀意志去初始化變量.初始化階
段是執(zhí)行類構(gòu)造器<clinit>() 方法的過程.<clinit>方法在以下情況下被執(zhí)行:
  1.當(dāng)遇到new,getstatic,putstatic或者invokestatic這四條字節(jié)碼指令的時候,如果該類沒有進(jìn)行
初始化,則需要先初始化.這四條指令對應(yīng)的是實(shí)例化對象,獲取一個靜態(tài)變量,設(shè)置一個靜態(tài)變量(常量
放在常量池中,不會觸發(fā)),或者調(diào)用靜態(tài)方法的時候.
  2.當(dāng)時候反射包的方法對類進(jìn)行反射調(diào)用的時候
  3.當(dāng)初始化一個類的時候,發(fā)現(xiàn)該類的父類還沒有進(jìn)行初始化,則初始其父類
  4.當(dāng)jvm啟動的時候,當(dāng)用戶指定執(zhí)行一個主類(就是包含main的那個類),虛擬機(jī)會先初始化這個類.

說明:1.類構(gòu)造器<clinit>方法是由編譯器自動收集所有類變量(static)
的賦值動作和靜態(tài)語句塊(static塊)中的語句合并產(chǎn)生的,收集的順序是由語句在源文件出現(xiàn)的順序決
定的,靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量,定義在他之后的變量不能訪問.
  2.類構(gòu)造器<clinit>方法與類的構(gòu)造函數(shù)(實(shí)例構(gòu)造函數(shù)是<init>)不同,他不需要顯式調(diào)用父類的<clinit>
方法,虛擬機(jī)會保證在調(diào)用子類的<clinit>方法之前父類該方法已經(jīng)被調(diào)用.
  3.<clinit>方法對于類或者接口來說,并不必須的,如果一個類沒有靜態(tài)語句,也沒有靜態(tài)變量,那么編
譯器可以不為這個類生成<clinit>方法.接口中不能使用靜態(tài)語句塊,并且在執(zhí)行接口的<clinit>方法
的時候,不需要先執(zhí)行父類的該方法,值有被定義的變量被使用的時候,才會初始化.接口的實(shí)現(xiàn)類在初始
化的時候也不會初始化該接口的<clinit>方法.
  4.虛擬機(jī)會保證一個類的<clinit>方法在多線程的環(huán)境下被正確的加鎖和同步,如果多個線程同時去初
始化一個類,那么只有一個線程執(zhí)行這個類的<clinit>方法,其他線程都需要阻塞等待.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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