詳解Java類加載機制

想聊Java的類加載機制就離不開Java類加載器不撑,這是Java語言的一個很重要的創(chuàng)新點臼勉,曾經(jīng)也是Java流行的重要原因邻吭。當初引入這個機制是為了滿足Java Applet開發(fā)的需求,簡單而言宴霸,就是為了能夠執(zhí)行從從遠程下載過來的的Java類囱晴,JVM咬咬牙引入了Java類加載機制膏蚓,后來的基于jvm的動態(tài)部署,插件化開發(fā)包括大家熱議的熱修復(熱修復其實也有不基于ClassLoader的解決方案畸写,有興趣請看我的熱修復初探)驮瞧,總之很多后來的技術都源于在JVM中引入了類加載器。

JVM:很慚愧艺糜,就做了一點微小的工作剧董,謝謝大家。

加載器

好了破停,講完了ClassLoader的來由翅楼,接下來可以正是介紹一下類加載器。如你所知真慢,當你寫完了一個.java文件的時候毅臊,編譯器會把他編譯成一個由字節(jié)碼組成的class文件,當程序運行時黑界,JVM會首先尋找包含有main()方法的類管嬉,把這個class文件中的字節(jié)碼數(shù)據(jù)讀入進來,轉(zhuǎn)化成JVM中運行時對應的Class對象朗鸠。執(zhí)行這個動作的蚯撩,就叫類加載器。

  • ClassLoader:是Java層幾乎所有類加載器的父類烛占,它定義了加載器的基本行為和加載動作

分類

類加載器分為可以大致分為:

  • Bootstrap ClassLoader(啟動類加載器)
    • 這個類加載器負責將一些核心的胎挎,被JVM識別的類加載進來,用C++實現(xiàn)忆家,與JVM是一體的犹菇。
  • Extension ClassLoader(擴展類加載器)
    • 這個類加載器用來加載 Java 的擴展庫
  • Applicaiton ClassLoader(應用程序類加載器)
    • 用于加載我們自己定義編寫的類
  • User ClassLoader (用戶自己實現(xiàn)的加載器)
    • 當實際需要自己掌控類加載過程時才會用到,一般沒有用到芽卿。

與之配套的加載機制就是“雙親委派模型”:

雙親委派模型

先看看Java類加載器的體系結構:


B55F.tmp.png

類加載邏輯代碼:

 protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            //首先檢查class是否已經(jīng)被加載
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //如果class沒有被加載且已經(jīng)設置parent,那么請求其父加載器加載
                    if (parent != null) {
                        /**
                         *注意當這里調(diào)用parent.loadClass()方法找不到Class時會拋出ClassNotFoundException異常揭芍,但是該異常是被捕獲的
                         */
                        c = parent.loadClass(name, false);
                    } else {
                      //如果沒有設定parent類加載器,則尋找BootstrapClss并嘗試使用Boot loader加載
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                /**
                 *如果當前這個loader所有的父加載器以及頂層的Bootstrap ClassLoader都不能加載待加載的類
                 *那么則調(diào)用自己的findClass()方法來加載
                 */                
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }


“雙親委派模型”簡單來說就是:

  • 1.先檢查需要加載的類是否已經(jīng)被加載卸例,如果沒有被加載称杨,則委托父加載器加載,父類繼續(xù)檢查筷转,嘗試請父類加載姑原,這個過程是從下-------> 上;
  • 2.如果走到頂層發(fā)現(xiàn)類沒有被加載過,那么會從頂層開始往下逐層嘗試加載旦装,這個過程是從上 ------> 下;

需要注意的幾個問題:

  • 1页衙,雙親XX 這種說法是有問題的,因為Java世界一直是單親家庭
  • 2,事實上加載器之間不是通過繼承店乐,而是通過組合的方式來實現(xiàn)整個加載過程艰躺,即每個加載器都持有上層加載器的引用,所以父加載器是一種籠統(tǒng)的說法眨八。

這里必須要提一提JVM如何判定兩個類你是否相等:

  • JVM除了比較類是否相等還要比較加載這兩個類的類加載器是否相等腺兴,只有同時滿足條件,兩個類才能被認定是相等的廉侧。

接下來問題來了页响,為什么雙親委派模型要有三層加載器而不是一層?

實際上段誊,三層類加載器代表了JVM對于待加載類的三個信任層次闰蚕,當需要加載一個全限定名為java.lang.Object的類時,JVM會首先信任頂層的引導類加載器连舍,即優(yōu)先用這個加載器嘗試加載没陡,如果不行,JVM會選擇繼續(xù)信任第二層的拓展類加載器索赏,往下盼玄,知道三層都無法加載,JVM才會選擇信任開發(fā)者自己定義的加載器潜腻。這種”父類“優(yōu)先的加載次序有效的防止了惡意代碼的加載埃儿。

總結

總而言之,雙親委派模型有效解決了以下問題:

  • 每一個類都只會被加載一次融涣,避免了重復加載
  • 每一個類都會被盡可能的加載(從引導類加載器往下童番,每個加載器都可能會根據(jù)優(yōu)先次序嘗試加載它)
  • 有效避免了某些惡意類的加載(比如自定義了Java。lang.Object類暴心,一般而言在雙親委派模型下會加載系統(tǒng)的Object類而不是自定義的Object類)

tips:可以說雙親委派模型主要是為了維護Java類加載的安全妓盲,防止惡意加載杂拨,與此配套的還有命名空間出有效的隔離,命名空間的作用抽象理解就是

  • 豎直方向上专普,父加載器中加載的類對于所有子加載器可見
  • 水平方向上,子類之間各自加載的類對于各自是不可間的(達到隔離效果)

基本上弹沽,日常的開發(fā)使用的都是使用系統(tǒng)提供的類加載器依照“雙親委派模型”來加載的檀夹,開發(fā)者基本接觸不到加載過程。但是當你要動態(tài)加載自己的外部的類的時候策橘,比如從網(wǎng)絡上下載的class文件炸渡,就需要自定義classloader來實現(xiàn)加載過程。

在Android中丽已,QQZone團隊提出的基于Dex分包的熱修復解決方案就屬于加載外部的類蚌堵,本來應當由開發(fā)者自己實現(xiàn)classloader來實現(xiàn)加載過程,但是Android本身已經(jīng)為我們封裝好了一個classloader,就是DexClassLoader(貼心~~)

事實上吼畏,如今Java中很多插件化開發(fā)督赤,動態(tài)部署,熱修復等動態(tài)技術都是基于Java的類加載器來展開的泻蚊。因此躲舌,我才會想專門用一篇文章總結Java的類加載器和加載機制。后面我會找時間基于HotFix詳細的分析其中的類加載過程性雄。畢竟理論總要落實到代碼才會讓人印象深刻没卸。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市秒旋,隨后出現(xiàn)的幾起案子约计,更是在濱河造成了極大的恐慌,老刑警劉巖迁筛,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件病蛉,死亡現(xiàn)場離奇詭異,居然都是意外死亡瑰煎,警方通過查閱死者的電腦和手機铺然,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酒甸,“玉大人魄健,你說我怎么就攤上這事〔迩冢” “怎么了沽瘦?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長农尖。 經(jīng)常有香客問我析恋,道長,這世上最難降的妖魔是什么盛卡? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任助隧,我火速辦了婚禮,結果婚禮上滑沧,老公的妹妹穿的比我還像新娘并村。我一直安慰自己,他們只是感情好滓技,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布哩牍。 她就那樣靜靜地躺著,像睡著了一般令漂。 火紅的嫁衣襯著肌膚如雪膝昆。 梳的紋絲不亂的頭發(fā)上丸边,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機與錄音荚孵,去河邊找鬼原环。 笑死,一個胖子當著我的面吹牛处窥,可吹牛的內(nèi)容都是我干的嘱吗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼滔驾,長吁一口氣:“原來是場噩夢啊……” “哼谒麦!你這毒婦竟也來了?” 一聲冷哼從身側響起哆致,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤绕德,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后摊阀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耻蛇,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年胞此,在試婚紗的時候發(fā)現(xiàn)自己被綠了臣咖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡漱牵,死狀恐怖夺蛇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酣胀,我是刑警寧澤刁赦,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏隔箍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一舱沧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸波闹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至孵淘,卻和暖如春蒲障,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工揉阎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留庄撮,地道東北人。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓毙籽,卻偏偏與公主長得像洞斯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子坑赡,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

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