《深入理解jvm》讀書筆記之——類加載器

## 類與類加載器

類加載器只用于實(shí)現(xiàn)類加載的動(dòng)作,但是同時(shí)還有著確保類的唯一性的作用。也就是說:比較兩個(gè)類是否相等,只有確保這兩個(gè)類在同一個(gè)類加載器的前提下才是有意義的。否則即使兩個(gè)類來源于同一個(gè)klass文件壕吹,被同一個(gè)jvm加載,只要他們的類加載器不同删铃,那么這兩個(gè)類就不相等(這里的相等代表klass對(duì)象的equals耳贬、isAssignableFrom、isInstance方法返回的結(jié)果)猎唁。

雙親委派模式

類加載器的種類

對(duì)于jvm來講咒劲,類加載器分為兩種:
一種是啟動(dòng)類加載器,使用c++實(shí)現(xiàn)诫隅,是jvm自身的一部分
另一種是所有其他的類加載器腐魂,使用java實(shí)現(xiàn),獨(dú)立于虛擬機(jī)外部逐纬,并且繼承自ClassLoader蛔屹。

然而對(duì)于java開發(fā)人員來講,類加載器可以分成這三種:

  1. 啟動(dòng)類加載器
  2. 擴(kuò)展類加載器
  3. 應(yīng)用程序類加載器豁生。這個(gè)類加載器也被稱為系統(tǒng)類加載器兔毒,負(fù)責(zé)加載用戶路徑上的類庫,開發(fā)者可以直接使用這個(gè)類加載器沛硅。

對(duì)于jvm加載一個(gè)類而言眼刃,是通過上面的三種類加載器相互配合加載的绕辖,他們之間的關(guān)系如下圖:

img

上圖中展示的這種一層層的層次加載關(guān)系摇肌,被稱為類的雙親委派模式。的雙親委派要求除了頂層的類加載器外都有自己的父類加載器仪际。這里的類加載器之間的父子關(guān)系一般都不會(huì)以繼承的關(guān)系來實(shí)現(xiàn)围小,而是組合關(guān)系來復(fù)用類加載器的代碼。

整個(gè)雙親委派模式的過程是:

如果一個(gè)類加載器收到了類加載請(qǐng)求树碱,他不會(huì)嘗試自己去加載肯适,而是吧這個(gè)請(qǐng)求委派給父類加載器去完成,每一個(gè)層次的類加載器都是如此成榜,因此所有類加載的請(qǐng)求最終都應(yīng)該傳到頂層的啟動(dòng)類加載器中框舔,只有當(dāng)父類加載器反饋?zhàn)约簾o法完成這個(gè)類加載的請(qǐng)求(它的搜索范圍中沒有找到所需的類)時(shí),子類才會(huì)嘗試自己加載。

為什么要使用雙親委派模式:因?yàn)檫@樣加載一個(gè)類刘绣,java類隨著他的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系樱溉,例如java.lang.Object他放在rt.jar中。無論哪個(gè)類加載器加載這個(gè)類纬凤,最后都是委派給最頂端的啟動(dòng)類加載器去加載福贞,這樣就保證了java.lang.Object無論是由哪個(gè)類加載器加載的,在當(dāng)前jvm下都是同一個(gè)類停士。

破壞雙親委派模型

  1. 第一次破壞是在jdk2之前挖帘,用戶自定義的類加載器都是重寫Classloader中的loadClass方法,這樣就導(dǎo)致每個(gè)自定義的類加載器其實(shí)是在使用自己的loadClass方法中的加載機(jī)制來進(jìn)行加載,這種模式當(dāng)然是不符合雙親委派機(jī)制的,也是無法保證同一個(gè)類在jvm中的唯一性的恋技,那么為了保證及時(shí)是由不同的類加載器(哪怕是用戶自定義的類加載器加載)也是唯一的拇舀,java官方在Classloader中添加了findClass方法,用戶只需要重新這個(gè)findClass方法,在loadClass方法的邏輯里猖任,如果父類加載失敗的時(shí)候你稚,才會(huì)調(diào)用自己的findClass方法來完成類加載,這樣就完成了符合雙親委派機(jī)制朱躺。

  2. 第二次的破壞是類似于jndi刁赖,jdbc這種服務(wù),因?yàn)檫@種服務(wù)需要回調(diào)用戶的代碼长搀,但是對(duì)于父類加載器而言是不認(rèn)識(shí)用戶的代碼的宇弛。

    那么這時(shí)候java團(tuán)隊(duì)使用了一個(gè)不太優(yōu)雅的設(shè)計(jì):線程上下文類加載器。這個(gè)類加載器可以通過Thread類的setContextClassLoader方法進(jìn)行設(shè)置,如果創(chuàng)建線程時(shí)還未設(shè)置源请,它就從父線程繼承一個(gè)枪芒,如果在應(yīng)用全局范圍內(nèi)都沒有設(shè)置過的話,那這個(gè)類加載器默認(rèn)就是應(yīng)用程序類加載器谁尸。

利用這個(gè)線程上下文類加載器,jdni去加載需要的spi代碼舅踪,也就是父類請(qǐng)求子類的加載器去加載。

  1. 第三次的破壞是因?yàn)橛脩魧?duì)于程序的動(dòng)態(tài)性追求良蛮,諸如:代碼熱替換抽碌,模塊熱部署。
    這時(shí)候就誕生了諸如jigsaw和osgi决瞳。對(duì)于現(xiàn)在的業(yè)界來講货徙,osgi贏得了java模塊化的主導(dǎo)權(quán),成為目前業(yè)界模塊化的標(biāo)準(zhǔn)皮胡。而Osgi模塊話的關(guān)鍵是他自己的類加載機(jī)制:每個(gè)程序模塊(bundle)都有自己的類加載器痴颊,需要更換程序(bundle)的時(shí)候,連同類加載器一起替換屡贺,以實(shí)現(xiàn)代碼的熱部署蠢棱。

osgi和雙親委派模式不同锌杀,他是一個(gè)基于網(wǎng)狀的互相組合依賴的加載。
Osgi的加載步驟是這樣的:

  1. 如果類或者資源是在包java.*中泻仙,那么交由父級(jí)類加載器代理完成抛丽,否則,搜索過程進(jìn)入第二步饰豺。如果父類級(jí)類加載器加載失敗亿鲜,那么查找過程結(jié)束,加載失敗冤吨。
  2. 如果類或者資源在啟動(dòng)代理序列(org.osgi.framework.bootdelegation)中定義蒿柳,那么交由父級(jí)代理完成,此時(shí)的父級(jí)代理有啟動(dòng)參數(shù)org.osgi.framework.bundle.parent指定漩蟆,默認(rèn)是引導(dǎo)類加載器(bootstrap class loader)垒探,如果找到了類或者資源,那么查找過程結(jié)束怠李。
  3. 如果類或者資源所在的包是在Import-Package中指定的圾叼,或者是在此之前通過動(dòng)態(tài)導(dǎo)入加載的了,那么將請(qǐng)求轉(zhuǎn)發(fā)到導(dǎo)出bundle的類加載器捺癞,否則搜索繼續(xù)進(jìn)行下一步夷蚊;如果該包在啟動(dòng)參數(shù)org.osgi.framework.system.packages.extra中,則將請(qǐng)求轉(zhuǎn)發(fā)給osgi容器外部的類加載器(通常是系統(tǒng)類加載器)髓介。如果將請(qǐng)求交由導(dǎo)出類加載器代理惕鼓,而類或者資源又沒有找到,那么查找過程中止唐础,同時(shí)請(qǐng)求失敗箱歧。
  4. 如果包中類或者和資源所在的包由其他bundle通過是使用Require-Bundle從一個(gè)或多個(gè)其他bundle進(jìn)行導(dǎo)入的了,那么請(qǐng)求交由其他那些bundle的類加載器完成一膨,按照根據(jù)在bundle的manifest中指定的順序進(jìn)行查找進(jìn)行查找呀邢。如果沒有找到類或者資源,搜索繼續(xù)進(jìn)行豹绪。
  5. 使用bundle本身的內(nèi)部bundle類路徑查找完畢之后价淌,。如果類或者資源還沒有找到森篷,搜索繼續(xù)到下一步输钩。
  6. 查找每一個(gè)附加的fragment的內(nèi)部類路徑豺型,fragment的查找根據(jù)bundle ID順序升序查找仲智。如果沒有找到類或者資源的,查找過程繼續(xù)下一步姻氨。
  7. 如果包中類或者資源所在的包由bundle導(dǎo)出钓辆,或者包由bundle導(dǎo)入(使用Import-Package或者Require-Bundle),查找結(jié)束,即類或者資源沒有找到前联。
  8. 否則功戚,如果類或者資源所在的包是通過使用DynamicImport-Package進(jìn)行導(dǎo)入,那么試圖進(jìn)行包的動(dòng)態(tài)導(dǎo)入似嗤。導(dǎo)出者exporter必須符合包約束啸臀。如果找到了合適的導(dǎo)出者exporter,然后建立連接烁落,以后的包導(dǎo)入就可以通過步驟三進(jìn)行乘粒。如果連接建立失敗,那么請(qǐng)求失敗伤塌。
  9. 如果動(dòng)態(tài)導(dǎo)入建立了灯萍,請(qǐng)求交由導(dǎo)出bundle的類加載器代理。如果代理查找失敗每聪,那么查找過程中止旦棉,請(qǐng)求失敗
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绑洛,一起剝皮案震驚了整個(gè)濱河市诊笤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讨跟,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡嗦董,警方通過查閱死者的電腦和手機(jī)奇唤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門廊勃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來隅居,“玉大人,你說我怎么就攤上這事涕蚤。” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我忧勿,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逐虚。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布注盈。 她就那樣靜靜地躺著,像睡著了一般浪慌。 火紅的嫁衣襯著肌膚如雪乌妒。 梳的紋絲不亂的頭發(fā)上汹想,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音撤蚊,去河邊找鬼侦啸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的私恬。 我是一名探鬼主播荣德,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼命爬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼曹傀!你這毒婦竟也來了饲宛?” 一聲冷哼從身側(cè)響起艇抠,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤幕庐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后家淤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體异剥,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年絮重,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了冤寿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡青伤,死狀恐怖督怜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狠角,我是刑警寧澤号杠,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響姨蟋,放射性物質(zhì)發(fā)生泄漏屉凯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一眼溶、第九天 我趴在偏房一處隱蔽的房頂上張望寝贡。 院中可真熱鬧盒粮,春花似錦痊银、人聲如沸衣屏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至羡玛,卻和暖如春别智,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稼稿。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工薄榛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人让歼。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓敞恋,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親谋右。 傳聞我的和親對(duì)象是個(gè)殘疾皇子硬猫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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