BiBi - JVM -8- 類(lèi)加載機(jī)制

From:深入理解Java虛擬機(jī)

Java天生的動(dòng)態(tài)擴(kuò)展語(yǔ)言特性是依賴(lài)運(yùn)行期動(dòng)態(tài)加載動(dòng)態(tài)連接這個(gè)特點(diǎn)實(shí)現(xiàn)的挡鞍。
例如:在編寫(xiě)一個(gè)面向接口的應(yīng)用程序,可以等到運(yùn)行時(shí)再指定其實(shí)際的實(shí)現(xiàn)類(lèi),用戶可以通過(guò)Java預(yù)定義的或自定義的類(lèi)加載器,讓一個(gè)本地的應(yīng)用程序可以在運(yùn)行時(shí)從網(wǎng)絡(luò)或其它地方加載一個(gè)二進(jìn)制流作為程序代碼的一部分讼育。

類(lèi)的生命周期

1)加載
2)連接:驗(yàn)證 -> 準(zhǔn)備 -> 解析
3)初始化
4)使用
5)卸載

其中【解析】階段在某些情況下可以在【初始化】階段之后再開(kāi)始峡竣,這是為了支持Java語(yǔ)言的運(yùn)行時(shí)綁定【動(dòng)態(tài)綁定】租谈。

有且只有5種情況必須立即進(jìn)行【初始化】

1)遇到new偷厦、getstatic、putstatic诀紊、invokestatic谒出,對(duì)應(yīng)的場(chǎng)景:使用new關(guān)鍵字實(shí)例化對(duì)象、讀取或設(shè)置一個(gè)類(lèi)的靜態(tài)字段【非final】邻奠、調(diào)用一個(gè)類(lèi)的靜態(tài)方法笤喳。
2)使用反射對(duì)類(lèi)進(jìn)行調(diào)用。
3)當(dāng)一個(gè)類(lèi)初始化時(shí)碌宴,先初始化其父類(lèi)杀狡。
4)當(dāng)虛擬機(jī)啟動(dòng)時(shí),用戶需要指定一個(gè)要執(zhí)行的主類(lèi)【包含main方法】贰镣,虛擬機(jī)會(huì)先初始化這個(gè)主類(lèi)呜象。
5)使用JDK1.7的動(dòng)態(tài)語(yǔ)言支持時(shí)膳凝,如果一個(gè)MethodHandler實(shí)例最后的解析結(jié)果是REF_getStatic、REF_putStatic恭陡、REF_invokeStatic的方法句柄蹬音。

  • 例子:
public class A {
  public static int value = 20;
  public static final String name = "ljg";
}

public class B extends A {

}

通過(guò)子類(lèi)引用父類(lèi)中的靜態(tài)字段,不會(huì)初始化子類(lèi)休玩。
即:B.value=33; 會(huì)實(shí)例化父類(lèi)A著淆,而不會(huì)實(shí)例化類(lèi)B。
對(duì)于靜態(tài)字段拴疤,只有直接定義這個(gè)字段的類(lèi)才會(huì)被初始化永部。

訪問(wèn)被final修飾的靜態(tài)字段,不會(huì)初始化該類(lèi)呐矾。
即:訪問(wèn)A.name時(shí)苔埋,不會(huì)實(shí)例化類(lèi)A。因?yàn)椋罕籪inal修飾蜒犯,已經(jīng)在編譯期把結(jié)果放入到常量池了组橄。

數(shù)組定義引用類(lèi),不會(huì)觸發(fā)該類(lèi)的初始化罚随。
即:A[ ] arr = new A[7]晨炕,不會(huì)實(shí)例化A。

  • 接口與類(lèi)的區(qū)別
    與上述中的【有且只有5種情況】毫炉,只有第3)種情況不同。一個(gè)類(lèi)初始化時(shí)削罩,要求先初始化其父類(lèi)瞄勾,但一個(gè)接口在初始化時(shí),并不要求其父接口全部都完成初始化弥激,只有在真正使用到父類(lèi)接口的時(shí)候才會(huì)初始化进陡。

1. 加載

加載階段虛擬機(jī)完成的3件事:
1)通過(guò)一個(gè)類(lèi)的全限定名來(lái)獲取定義此類(lèi)的二進(jìn)制字節(jié)流。 獲取的方式有:
??(1)從ZIP包中讀取微服,如:jar趾疚、ear、war以蕴。
??(2)從網(wǎng)絡(luò)中獲取糙麦,如:Applet。
??(3)運(yùn)行時(shí)計(jì)算生成丛肮,如:動(dòng)態(tài)代理赡磅。
??(4)其它文件生成,如:由JSP文件生成對(duì)應(yīng)的Class類(lèi)宝与。
2)將這個(gè)字節(jié)流代表的結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu)焚廊。
3)在內(nèi)存中生成一個(gè)代表這個(gè)類(lèi)的Class對(duì)象冶匹,作為方法區(qū)中這個(gè)類(lèi)的各種數(shù)據(jù)的訪問(wèn)入口∨匚粒【該Class對(duì)象可以在Java堆中嚼隘,也可以像HotSpot虛擬機(jī)那樣放在方法區(qū)】

數(shù)組類(lèi)本身不通過(guò)類(lèi)加載器創(chuàng)建,它由Java虛擬機(jī)直接創(chuàng)建袒餐。但數(shù)組類(lèi)的元素是由類(lèi)加載器去創(chuàng)建飞蛹。

2. 驗(yàn)證

為了確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全匿乃。Class文件并不一定要求用Java源碼編譯而出來(lái)桩皿,比如:用十六進(jìn)制編輯器直接編寫(xiě)產(chǎn)生Class文件,可能會(huì)存在數(shù)組越界幢炸,類(lèi)型轉(zhuǎn)換異常的問(wèn)題泄隔,盡管使用Java編譯器編譯時(shí),會(huì)拒絕編譯宛徊。

驗(yàn)證階段對(duì)虛擬機(jī)來(lái)說(shuō)不是必要的佛嬉,對(duì)于已經(jīng)被反復(fù)使用和驗(yàn)證過(guò)的代碼,可以通過(guò)參數(shù)來(lái)關(guān)閉類(lèi)的驗(yàn)證闸天,以縮短虛擬機(jī)類(lèi)加載的時(shí)間暖呕。

3. 準(zhǔn)備

準(zhǔn)備階段正式為【類(lèi)變量】分配內(nèi)存并設(shè)置【類(lèi)變量初始值】的階段。注意:這時(shí)只對(duì)類(lèi)變量【被static修飾的變量】進(jìn)行內(nèi)存分配苞氮,在【方法區(qū)】中進(jìn)行分配湾揽。這個(gè)階段不包含實(shí)例變量

public static int value = 123;

準(zhǔn)備階段只是為value設(shè)初始值0而不是123笼吟,因?yàn)檫@時(shí)候尚未開(kāi)始執(zhí)行任何Java方法库物,而把value賦值為123的putstatic指令是程序被編譯后,存放于類(lèi)構(gòu)造器<clinit>()方法中贷帮,所以把value賦值為123的動(dòng)作將在【初始化】階段執(zhí)行戚揭。

public static final int value = 123;

由于被final修飾珍策,編譯時(shí)javac將會(huì)為value生成ConstantValue屬性姑隅,在準(zhǔn)備階段虛擬機(jī)就會(huì)根據(jù)ConstantValue的設(shè)置將value賦值為123为迈。
所以由final修飾的static靜態(tài)常量叙淌,在準(zhǔn)備階段就會(huì)賦用戶定義的值多矮。

4. 解析

解析階段是虛擬機(jī)將常量池內(nèi)的【符號(hào)引用替換為直接引用】的過(guò)程躯砰。
invokedynamic指令用于動(dòng)態(tài)語(yǔ)言支持活箕,必須等到程序?qū)嶋H運(yùn)行到這條指令的時(shí)候魂仍,解析動(dòng)作才能進(jìn)行沃但。相對(duì)的刮便,其余可觸發(fā)解析的指令都是【靜態(tài)】的,可以在剛剛完成加載階段绽慈,還沒(méi)有開(kāi)始執(zhí)行代碼時(shí)就進(jìn)行解析恨旱。

5. 初始化

類(lèi)初始化階段是類(lèi)加載過(guò)程的最后一步辈毯,前面的類(lèi)加載過(guò)程中,除了在加載階段用戶應(yīng)用程序可以通過(guò)自定義類(lèi)加載器參與之外搜贤,其余都是有虛擬機(jī)主導(dǎo)和控制谆沃。初始化階段才是真正執(zhí)行Java程序代碼。在準(zhǔn)備階段仪芒,變量已經(jīng)賦過(guò)一次系統(tǒng)要求的初始值唁影,在初始化階段則是初始化程序員主動(dòng)計(jì)劃的賦值。

類(lèi)構(gòu)造器<clinit>()
實(shí)例構(gòu)造器<init>()

<clinit>()方法是由編譯器自動(dòng)收集類(lèi)中的所有【類(lèi)變量掂名,非final修飾】的賦值動(dòng)作和【靜態(tài)語(yǔ)句塊static{ }】中的語(yǔ)句合并產(chǎn)生的据沈,收集的順序由語(yǔ)句在源文件中出現(xiàn)的順序決定〗让铮【靜態(tài)語(yǔ)句塊中只能訪問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量锌介,對(duì)于定義在它之后的變量,只能賦值猾警,但不能訪問(wèn)】孔祸。如:

public class Test {
  static {
    i = 0; //ok,可以對(duì)i進(jìn)行賦值
    System.out.print(i); //error发皿,不能訪問(wèn)
  }
  static int i = 1;
}

<clinit>()方法崔慧,虛擬機(jī)會(huì)保證先執(zhí)行父類(lèi)中的方法,而不需要顯示調(diào)用穴墅。因此虛擬機(jī)中第一個(gè)被執(zhí)行的<clinit>()方法的類(lèi)肯定是Object惶室。

接口中不能使用靜態(tài)語(yǔ)句塊,并且執(zhí)行接口的<clinit>()方法不需要先執(zhí)行父接口的<clinit>()方法玄货。只有當(dāng)父接口中定義的變量使用時(shí)拇涤,父接口才會(huì)初始化。另外誉结,接口的實(shí)現(xiàn)類(lèi)在初始化時(shí)也一樣不會(huì)執(zhí)行接口的<clinit>()方法。

虛擬機(jī)會(huì)保證一個(gè)類(lèi)的<clinit>()方法在多線程環(huán)境中被正確的加鎖券躁、同步惩坑,如果多個(gè)線程同時(shí)去初始化一個(gè)類(lèi),那么只會(huì)有一個(gè)線程去執(zhí)行這個(gè)類(lèi)的<clinit>()方法也拜,其他線程都阻塞等待以舒,直到活動(dòng)線程執(zhí)行<clinit>()方法完畢。執(zhí)行完畢后慢哈,其它線程不會(huì)再執(zhí)行<clinit>()方法蔓钟,因?yàn)橥粋€(gè)類(lèi)加載器下,一個(gè)類(lèi)型只會(huì)初始化一次卵贱。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末滥沫,一起剝皮案震驚了整個(gè)濱河市侣集,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌兰绣,老刑警劉巖世分,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缀辩,居然都是意外死亡臭埋,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)臀玄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)瓢阴,“玉大人,你說(shuō)我怎么就攤上這事健无∪倏郑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵睬涧,是天一觀的道長(zhǎng)募胃。 經(jīng)常有香客問(wèn)我,道長(zhǎng)畦浓,這世上最難降的妖魔是什么痹束? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮讶请,結(jié)果婚禮上祷嘶,老公的妹妹穿的比我還像新娘。我一直安慰自己夺溢,他們只是感情好论巍,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著风响,像睡著了一般嘉汰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上状勤,一...
    開(kāi)封第一講書(shū)人閱讀 49,792評(píng)論 1 290
  • 那天鞋怀,我揣著相機(jī)與錄音,去河邊找鬼持搜。 笑死密似,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的葫盼。 我是一名探鬼主播残腌,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了抛猫?” 一聲冷哼從身側(cè)響起蟆盹,我...
    開(kāi)封第一講書(shū)人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邑滨,沒(méi)想到半個(gè)月后日缨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掖看,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年匣距,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哎壳。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毅待,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出归榕,到底是詐尸還是另有隱情尸红,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布刹泄,位于F島的核電站外里,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏特石。R本人自食惡果不足惜盅蝗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姆蘸。 院中可真熱鬧墩莫,春花似錦、人聲如沸逞敷。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)推捐。三九已至裂问,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牛柒,已是汗流浹背堪簿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留焰络,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓符喝,卻偏偏與公主長(zhǎng)得像闪彼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348