ClassLoader機制詳解

定義:類加載器
ClassLoader作用:把class加載到jvm,程序可正常運行。
jvm啟動時,并不會一次性加載所有的class,而是選擇動態選擇加載(防止一次性加載太多,內存壓力太大)。

Java語言自帶的三個類加載器:

Bootstrap ClassLoader :
最頂層加載器,主要加載核心類庫(%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等)。
Extention ClassLoader:
擴展類加載器(%JRE_HOME%\lib\ext目錄下的jar包和class文件)
Appclass Loader:
也稱為SystemAppClass ,加載當前應用的classpath的所有類

執行順序:從上到下依次執行。

通過sun.misc.Launcher類的源碼,可以的到相關信息:
1,Launcher初始化了ExtClassLoader和AppClassLoader。
2,Launcher中并沒有看見BootstrapClassLoader,但通過System.getProperty("sun.boot.class.path")得到了字符串bootClassPath,這個應該就是BootstrapClassLoader加載的jar包路徑。這個路徑的內容全是jre目錄下的jar與class文件

BootstrapClassLoader 測試
System.out.println(System.getProperty("sun.boot.class.path"));
得到:
C:\Program Files\Java\jre1.8.0_91\lib\resources.jar; 
C:\Program Files\Java\jre1.8.0_91\lib\rt.jar; 
C:\Program Files\Java\jre1.8.0_91\lib\sunrsasign.jar; 
C:\Program Files\Java\jre1.8.0_91\lib\jsse.jar; 
C:\Program Files\Java\jre1.8.0_91\lib\jce.jar; 
C:\Program Files\Java\jre1.8.0_91\lib\charsets.jar; 
C:\Program Files\Java\jre1.8.0_91\lib\jfr.jar; 
C:\Program Files\Java\jre1.8.0_91\classes
ExtClassLoader 測試
System.out.println(System.getProperty("java.ext.dirs"));
得到:
C:\Program Files\Java\jre1.8.0_91\lib\ext;C:\Windows\Sun\Java\lib\ext
AppClassLoader 測試
System.out.println(System.getProperty("java.class.path"));
打印得到:
D:\workspace\ClassLoaderDemo\bin

每個類加載器都有一個父加載器

ClassLoader cl = Test.class.getClassLoader(); 
System.out.println("ClassLoader is:"+cl.toString()); 
System.out.println("ClassLoader\'s parent is:"+cl.getParent().toString());
得到:
ClassLoader is:sun.misc.Launcher$AppClassLoader@73d16e93 
ClassLoader's parent is:sun.misc.Launcher$ExtClassLoader@15db9742

AppClassLoader的父加載器是ExtClassLoader,
可以通過getParent()方法獲取。
父加載器不是父類

static class ExtClassLoader extends URLClassLoader {} 
static class AppClassLoader extends URLClassLoader {}

ExtClassLoader和AppClassLoader同樣繼承自URLClassLoader。


image.png

getParent()方法在ClassLoader當中。
getParent分為兩種情況:
1.如果構造方法當中有賦值指定,則parent為指定。
2.如果創建時沒有指定,則通過Launcher.getClassLoader獲取,則默認為AppClassLoader。
AppClassLoader的parent是ExtClassLoader,ExtClassLoader的parent是null.

Bootstrap ClassLoader是由C/C++編寫,并不是一個java類,無法在java代碼中獲得引用。它沒有父類也就是沒有父容器,它可以作為ClassLoader的父加載器。

雙親委托:

雙親委托
一個類加載器加載class或resouces時,是通過委托模式。
首先判斷是否已經加載成功,
    如果么有(先不自己找),通過父加載器,然后遞歸下去,直到BootstrapClassLoader,
        找到直接返回,
        沒有找到則一級級(ExtClassLoader->AppClassLoader-->...)返回直到找到對象。
image.png
  1. 一個AppClassLoader查找資源時,先看看緩存是否有,緩存有從緩存中獲取,否則委托給父加載器。
  2. 遞歸,重復第1部的操作。
  3. 如果ExtClassLoader也沒有加載過,則由Bootstrap ClassLoader出面,它首先查找緩存,如果沒有找到的話,就去找自己的規定的路徑下,也就是sun.mic.boot.class下面的路徑。找到就返回,沒有找到,讓子加載器自己去找。
  4. Bootstrap ClassLoader如果沒有查找成功,則ExtClassLoader自己在java.ext.dirs路徑中去查找,查找成功就返回,查找不成功,再向下讓子加載器找。
  5. ExtClassLoader查找不成功,AppClassLoader就自己查找,在java.class.path路徑下查找。找到就返回。如果沒有找到就讓子類找,如果沒有子類會怎么樣?拋出各種異常。

加載詳情圖:


image.png

重要方法:

loadClass()

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 首先,檢測是否已經加載
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        //父加載器不為空則調用父加載器的loadClass
                        c = parent.loadClass(name, false);
                    } else {
                        //父加載器為空則調用Bootstrap Classloader
//這也解釋了ExtClassLoader的parent為null,但仍然說Bootstrap ClassLoader是它的父加載器。 
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //父加載器沒有找到,則調用findclass
                    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(),生成最終的Class對象。
                resolveClass(c);
            }
            return c;
        }
    }

上面的代碼也解釋了雙親委托機制。

自定義classLoader建議:覆蓋findClass()方法,而不要直接改寫loadClass()方法。

用途:
這些類加載器默認只能加載指定目錄的jar或資源文件。當你想要突破內置加載器的限制時,可以玩出花樣來。
如果要想動態加載某些磁盤的資源,或通過網絡下載class后加載,這樣就可以自定義classLoader去做這些事。

步驟:

    1. 編寫一個類繼承自ClassLoader抽象類。
    2. 復寫它的findClass()方法。
    3. 在findClass()方法中調用defineClass()。

defineClass()能將class二進制內容轉換成Class對象,如果不符合要求的會拋出各種異常。
注意點:一個ClassLoader在創建時如果沒有指定parent,則parent默認為AppClassLoader,這樣就能夠保證它能訪問系統內置加載器加載成功的class文件。

常見用途:將一個class用特定規則加密,然后在自定義的ClassLoader進行解密后在程序中加載使用。只有在我們自定義的加載器里能解密,提高了程序安全性。

Context ClassLoader 線程上下文類加載器
每個Thread都有一個相關聯的ClassLoader,默認是AppClassLoader。并且子線程默認使用父線程的ClassLoader除非子線程特別設置。(Thread.currentThread().setContextClassLoader(xxxLoader);)

總結:
1. ClassLoader用來加載class文件的。
2. 系統內置的ClassLoader通過雙親委托來加載指定路徑下的class和資源。
3. 可以自定義ClassLoader一般覆蓋findClass()方法。
4. ContextClassLoader與線程相關,可以獲取和設置,可以繞過雙親委托的機制。

詳情參考:http://blog.csdn.net/briblue/article/details/54973413

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容