讀深入理解Java虛擬機(jī)筆記--類加載機(jī)制

? 已經(jīng)讀到《深入理解java虛擬機(jī)》第三部分了,感覺開始飄了婚陪,太枯燥了這部分族沃,不過還是跟著書上走了一遍,大概了解了其內(nèi)容泌参,這部分內(nèi)容主要類文件結(jié)構(gòu)脆淹,類加載機(jī)制,執(zhí)行引擎等組成沽一。本次筆記主要記錄類加載過程盖溺。

? 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn)铣缠、轉(zhuǎn)換解析烘嘱、初始化,最終形成可以直接被Java虛擬機(jī)使用的Java類型蝗蛙,這就是虛擬機(jī)的類加載機(jī)制蝇庭。

1丘损、類加載步驟

? 類從被加載到內(nèi)存到使用完成被卸載出內(nèi)存及老,需要經(jīng)歷加載爆惧、連接呻引、初始化沙廉、使用产徊、卸載這幾個(gè)過程员串,其中連接又可以細(xì)分為驗(yàn)證厚宰、準(zhǔn)備泰涂、解析鲫竞。


類的生命周期

(1)加載

? 在加載階段,虛擬機(jī)主要完成三件事情:

① 通過一個(gè)類的全限定名(比如com.danny.framework.t)來獲取定義該類的二進(jìn)制流逼蒙;

② 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)存儲(chǔ)結(jié)構(gòu)从绘;

③ 在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對(duì)象,作為程序訪問方法區(qū)中這個(gè)類的外部接口。

(2)驗(yàn)證

? 驗(yàn)證的目的是為了確保class文件的字節(jié)流包含的內(nèi)容符合虛擬機(jī)的要求僵井,且不會(huì)危害虛擬機(jī)的安全陕截。從整體上看,驗(yàn)證階段大致會(huì)完成下面四個(gè)階段的檢驗(yàn)動(dòng)作:

①文件格式驗(yàn)證:主要驗(yàn)證class文件中二進(jìn)制字節(jié)流的格式批什,比如魔數(shù)是否已0xCAFEBABY開頭农曲、版本號(hào)是否正確等。

②元數(shù)據(jù)驗(yàn)證:主要對(duì)字節(jié)碼描述的信息進(jìn)行語義分析驻债,保證其符合Java語言規(guī)范乳规,比如驗(yàn)證這個(gè)類是否有父類(java.lang.Object除外),如果這個(gè)類不是抽象類合呐,是否實(shí)現(xiàn)了父類或接口中沒有實(shí)現(xiàn)的方法暮的,等等。

③字節(jié)碼驗(yàn)證:字節(jié)碼驗(yàn)證更為高級(jí)淌实,通過數(shù)據(jù)流和控制流分析冻辩,確保程序是合法的、符合邏輯的拆祈。

④符號(hào)引用驗(yàn)證:對(duì)類自身以外的信息進(jìn)行匹配性校驗(yàn)恨闪,舉個(gè)栗子,比如通過類的全限定名能否找到對(duì)應(yīng)類放坏、在類中能否找到字段名/方法名對(duì)應(yīng)的字段/方法咙咽,如果符號(hào)引用驗(yàn)證失敗,將拋出“java.lang.NoSuchFieldError”轻姿、“java.lang.NoSuchMethodError”等異常犁珠。

(3)準(zhǔn)備

? 正式為類變量分配內(nèi)存并設(shè)置類變量初始值,這些變量所使用的內(nèi)存都分配在方法區(qū)互亮。注意分配內(nèi)存的對(duì)象是“類變量”而不是實(shí)例變量犁享,而且為其分配的是“初始值”,一般數(shù)值類型的初始值都為0豹休,char類型的初始值為’\u0000’(常量池中一個(gè)表示Nul的字符串)炊昆,boolean類型初始值為false,引用類型初始值為null威根。

? 但是加上final關(guān)鍵字比如public static final int value=998;在準(zhǔn)備階段會(huì)初始化value的值為998凤巨;

(4)解析

? 解析是將常量池中符號(hào)引用替換為直接引用的過程。

? 符號(hào)引用是以一組符號(hào)來描述所引用的目標(biāo)洛搀,符號(hào)引用與虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無關(guān)敢茁,引用的目標(biāo)不一定已經(jīng)加載到內(nèi)存中。

? 直接引用可以是直接指向目標(biāo)的指針留美、相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄彰檬。直接引用和虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局有關(guān)伸刃,如果有了直接引用,那引用的目標(biāo)一定在內(nèi)存中存在逢倍。

? 解析的時(shí)候class已經(jīng)被加載到方法區(qū)的內(nèi)存中捧颅,因此要把符號(hào)引用轉(zhuǎn)化為直接引用,也就是能直接找到該類實(shí)際內(nèi)存地址的引用较雕。

(5)初始化

? 類初始化階段是類加載階段的最后一步碉哑,前面的類加載過程中,除了在加載階段用戶應(yīng)用程序可以通過自定義類加載器參與之外亮蒋,其余動(dòng)作完全由虛擬機(jī)主導(dǎo)和控制扣典。到了初始化階段才真正執(zhí)行類中定義的Java程序代碼(或者說字節(jié)碼)。

? 在準(zhǔn)備階段慎玖,變量已經(jīng)賦過一次系統(tǒng)要求的初始值激捏,而在初始化階段則根據(jù)程序員通過程序制定的主觀計(jì)劃去初始化類變量和其他資源,或者可以從另外一個(gè)角度表達(dá):初始化階段是執(zhí)行類構(gòu)造器<clinit>()方法的過程凄吏。<clinit>()方法是由編譯器自動(dòng)收集類中所有類變量的賦值動(dòng)作和靜態(tài)語句塊(static{})中的語句合并產(chǎn)生的,編譯器收集的順序是由語句在源文件中出現(xiàn)的順序決定的闰蛔,靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量痕钢,定義在它之后的變量,在前面的靜態(tài)語句塊可以賦值序六,但是不能訪問.

? 下面看段代碼來理解下:


? 運(yùn)行結(jié)果如下:


? 上面的例子中可以看到一個(gè)類從加載到實(shí)例化的過程中任连,靜態(tài)代碼塊、構(gòu)造方法例诀、非靜態(tài)代碼塊的加載順序随抠。

? 虛擬機(jī)會(huì)保證一個(gè)類的<clinit>()方法在多線程環(huán)境下被正確的加鎖、同步繁涂,如果多個(gè)線程同時(shí)去初始化一個(gè)類拱她,那么只會(huì)有一個(gè)線程去執(zhí)行這個(gè)類的<clinit>()方法,其他線程都需要阻塞等待扔罪,知道活動(dòng)線程執(zhí)行<clinit>()方法完畢秉沼。如果在一個(gè)類的<clinit>()方法中有耗時(shí)很長(zhǎng)的操作,就可能造成多個(gè)線程阻塞矿酵,在實(shí)際應(yīng)用中這種阻塞往往是很隱蔽的唬复。下面代碼演示了這種場(chǎng)景。


? 運(yùn)行結(jié)果如下全肮,即一條線程在死循環(huán)以模擬長(zhǎng)時(shí)間操作敞咧,另外一條線程在阻塞等待。


2辜腺、類加載器

2.1 類與類加載器

? 虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)把類加載階段中的“通過一個(gè)類的全限定名來獲取描述此類的二進(jìn)制字節(jié)流”這個(gè)動(dòng)作放到虛擬機(jī)外部去實(shí)現(xiàn)休建,以便讓應(yīng)用程序自己決定如何去獲取所需要的類乍恐,實(shí)現(xiàn)這個(gè)動(dòng)作的代碼模塊稱為“類加載器”。類加載器除了有加載類的作用丰包,還有一個(gè)舉足輕重的作用禁熏,對(duì)于每一個(gè)類,都需要由加載它的加載器和這個(gè)類本身共同確立這個(gè)類在Java虛擬機(jī)中的唯一性邑彪。也就是說瞧毙,兩個(gè)相同的類,只有是在同一個(gè)加載器加載的情況下才“相等”寄症,這里的“相等”是指代表類的Class對(duì)象的equals()方法宙彪、isAssignableFrom()方法、isInstance()方法的返回結(jié)果有巧,也包括instanceof關(guān)鍵字對(duì)對(duì)象所屬關(guān)系的判定結(jié)果释漆。下面是演示代碼:


? 運(yùn)行結(jié)果:

? 可以看到 ,這個(gè)對(duì)象與類test.Test做所屬類型檢查的時(shí)候卻返回了false篮迎,這是因?yàn)樘摂M機(jī)中存在了兩個(gè)Test類男图,一個(gè)是由系統(tǒng)應(yīng)用程序類加載的,另一個(gè)是由我們自定義的類加載器加載的甜橱,雖然都來自同一個(gè)Class文件逊笆,但依然是兩個(gè)獨(dú)立的類,做對(duì)象所屬類型檢查時(shí)結(jié)果自然為false岂傲。

2.2 雙親委派模型

? 從虛擬機(jī)的角度來講难裆,只存在兩種不同的類加載器:一種是啟動(dòng)類加載器(Bootstrap ClassLoader),這個(gè)類加載器使用C++語言實(shí)現(xiàn)镊掖,是虛擬機(jī)自身的一部分乃戈;另一種就是所有其他的類加載器,這些類加載器都是由Java語言實(shí)現(xiàn)亩进,獨(dú)立于虛擬機(jī)外部症虑,并且全都繼承自抽象類java.lang.ClassLoader。但是從java開發(fā)人員的角度來看归薛,類加載器還可以劃分的更細(xì)一些侦讨。

① 啟動(dòng)類加載器(Bootstrap ClassLoader)是由C/C++編譯而來的,主要負(fù)責(zé)加載JAVA_HOME\lib目錄或者被-Xbootclasspath參數(shù)指定目錄中的部分類苟翻,具體加載哪些類可以通過“System.getProperty(“sun.boot.class.path”)”來查看韵卤。

② 擴(kuò)展類加載器(Extension ClassLoader)由sun.misc.Launcher.ExtClassLoader實(shí)現(xiàn),負(fù)責(zé)加載JAVA_HOME\lib\ext目錄或者被java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(kù)崇猫,可以用通過“System.getProperty(“java.ext.dirs”)”來查看具體都加載哪些類沈条。

③ 應(yīng)用程序類加載器(Application ClassLoader)由sun.misc.Launcher.AppClassLoader實(shí)現(xiàn),負(fù)責(zé)加載用戶類路徑(我們通常指定的classpath)上的類诅炉,如果程序中沒有自定義類加載器蜡歹,應(yīng)用程序類加載器就是程序默認(rèn)的類加載器屋厘。

? 應(yīng)用程序都是由這3種類加載相互配合進(jìn)行加載的,如果有必要月而,還可以加入自定義的類加載器汗洒。

? 類加載器之間的層次關(guān)系,稱為類加載器的雙親委派模型(Parents Delegation Model)父款。雙親委派模型要求除了頂層的啟動(dòng)類加載器外溢谤,其余的類加載器都應(yīng)有自己的父類加載器。這里類加載器之間的父子關(guān)系一般不會(huì)以繼承的關(guān)系來實(shí)現(xiàn)憨攒,而是都使用組合關(guān)系來復(fù)用父類加載器的代碼世杀。

? 雙親委派模型的工作方式是:如果一個(gè)類加載器收到了子類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類肝集,而是把這個(gè)請(qǐng)求委派給父類加載器去完成瞻坝,每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最后都應(yīng)該傳送到頂層的啟動(dòng)類加載器中杏瞻,只有當(dāng)父類加載器反饋?zhàn)约簾o法完成這個(gè)加載請(qǐng)求(它的搜索范圍沒有找到所需的類)時(shí)所刀,子類加載器才會(huì)嘗試去自己加載。


? 再來看它的實(shí)現(xiàn)代碼捞挥,實(shí)現(xiàn)雙親委派的代碼都集中在java.lang.ClassLoader的loadClass()方法之中勉痴,如下面代碼所示:


? 這段代碼的主要意思就是先檢查是否已經(jīng)被加載過,若沒有加載則調(diào)用父類加載器的loadClass()方法树肃,若父類加載器為空則默認(rèn)使用啟動(dòng)類加載器作為父加載器。如果父類加載器加載失敗瀑罗,拋出ClassNotFoundException異常后胸嘴,再調(diào)用自己的findClass()方法進(jìn)行加載。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斩祭,一起剝皮案震驚了整個(gè)濱河市劣像,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌摧玫,老刑警劉巖耳奕,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異诬像,居然都是意外死亡屋群,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門坏挠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來芍躏,“玉大人,你說我怎么就攤上這事降狠《钥ⅲ” “怎么了庇楞?”我有些...
    開封第一講書人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)否纬。 經(jīng)常有香客問我吕晌,道長(zhǎng),這世上最難降的妖魔是什么临燃? 我笑而不...
    開封第一講書人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任睛驳,我火速辦了婚禮,結(jié)果婚禮上谬俄,老公的妹妹穿的比我還像新娘柏靶。我一直安慰自己,他們只是感情好溃论,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開白布屎蜓。 她就那樣靜靜地躺著,像睡著了一般钥勋。 火紅的嫁衣襯著肌膚如雪炬转。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,837評(píng)論 1 290
  • 那天算灸,我揣著相機(jī)與錄音扼劈,去河邊找鬼。 笑死菲驴,一個(gè)胖子當(dāng)著我的面吹牛荐吵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赊瞬,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼先煎,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了巧涧?” 一聲冷哼從身側(cè)響起薯蝎,我...
    開封第一講書人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谤绳,沒想到半個(gè)月后占锯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缩筛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年消略,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瞎抛。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疑俭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钞艇,我是刑警寧澤啄寡,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站哩照,受9級(jí)特大地震影響挺物,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜飘弧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一识藤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧次伶,春花似錦痴昧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至柱彻,卻和暖如春豪娜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哟楷。 一陣腳步聲響...
    開封第一講書人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工瘤载, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人卖擅。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓鸣奔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親惩阶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挎狸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容