Java類加載過程
1. 加載(loading)
主要分為三個(gè)步驟:
- 通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流余蟹。
- 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)卷胯。
- 在Java堆中生成一個(gè)代表這個(gè)列的java.lang.Class對(duì)象,作為方法區(qū)這些數(shù)據(jù)的訪問入口威酒。
2. 鏈接(linking)
2.1 驗(yàn)證(verification)
主要包括:
- 文件格式驗(yàn)證
- 元數(shù)據(jù)驗(yàn)證(語義校驗(yàn)窑睁,保證符合Java語義規(guī)范)
- 字節(jié)碼驗(yàn)證
- 符號(hào)引用驗(yàn)證(檢查類,字段葵孤,方法是否存在担钮,訪問限制等等)
2.2 準(zhǔn)備(preparation)
為類的靜態(tài)變量分配內(nèi)存和初始化。這些內(nèi)存在方法區(qū)分配尤仍,初始化的值“通常情況”下是數(shù)據(jù)類型的零值箫津。假設(shè)一個(gè)類變量定義為:
public static int value = 123;
那么value將被初始化為0,上面的賦值操作將在<cinit>()方法中進(jìn)行。
假設(shè)一個(gè)類變量定義為(常量):
public static final int value = 123;
那么value將被初始化為123苏遥。
2.3 符號(hào)解析(resolution of Symbolic References)
一個(gè)Java類中通常會(huì)包含對(duì)其他類或接口的引用饼拍,解析過程就是確保這些被引用的類能被正確的找到。解析的過程可能會(huì)導(dǎo)致其他Java類被加載田炭。被引用類的加載由引用類的類加載器執(zhí)行师抄。
不同的JVM實(shí)現(xiàn)可能選擇不同的解析策略。一種做法是在鏈接的時(shí)候教硫,就遞歸的把所有依賴的形式引用都進(jìn)行解析司澎。而另外的做法則可能是只在一個(gè)形式引用真正需要的時(shí)候才進(jìn)行解析。
3. 初始化(initialization)
虛擬機(jī)規(guī)范嚴(yán)格規(guī)定有且只有四種情況必須對(duì)類進(jìn)行初始化:
遇到new栋豫,getstatic挤安,putstatic,或invokestatic這4條字節(jié)碼指令的時(shí)候丧鸯。
使用java.lang.reflect包的方法對(duì)類進(jìn)行反射調(diào)用的時(shí)候蛤铜。
當(dāng)初始化一個(gè)類的時(shí)候,如果其父類還沒有進(jìn)行過初始化丛肢,則需要先觸發(fā)其父類的初始化围肥。
虛擬機(jī)啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的類蜂怎,虛擬機(jī)將初始化這個(gè)類穆刻。
初始化是執(zhí)行類構(gòu)造器<cinit>()的過程。
<cinit>()方法由靜態(tài)變量賦值操作和靜態(tài)語句塊合并而成杠步,合成順序按照文件中出現(xiàn)的順序氢伟。編譯器執(zhí)行該操作。
虛擬機(jī)保證初始化過程在多線程中被正確的加鎖和同步幽歼。
虛擬機(jī)保證子類的<cinit>()執(zhí)行之前朵锣,父類的<cinit>()已經(jīng)執(zhí)行完畢。
執(zhí)行接口的<cinit>()方法不需要先執(zhí)行父接口的<cinit>()方法甸私,接口的實(shí)現(xiàn)類在初始化時(shí)也一樣不會(huì)先執(zhí)行接口的<cinit>()方法诚些。