ClassLoader淺析(一) —— Java ClassLoader

  • ClassLoader的具體作用就是將字節(jié)碼格式文件加載到虛擬機(jī)中去婶博。Java中是把class文件加載到JVM荡陷。Android中是把dex/odex文件加載入虛擬機(jī)豆励。
  • 當(dāng)JVM啟動(dòng)的時(shí)候诵次,不會(huì)一下子把所有的class文件加載進(jìn)JVM薛夜,而是根據(jù)需要去動(dòng)態(tài)加載速梗。

JAVA類加載

  • 在Java中有三個(gè)類加載器
    1. Bootstrap ClassLoader:啟動(dòng)類加載器肮塞,最頂層的加載類襟齿。負(fù)責(zé)加載JDK中的核心類庫(kù),如:rt.jar枕赵、resources.jar猜欺、charsets.jar等。他是由C++實(shí)現(xiàn)的拷窜,并不繼承自 java.lang.ClassLoader开皿。
    2. Extention ClassLoader:擴(kuò)展類加載器,負(fù)責(zé)加載Java的擴(kuò)展類庫(kù)篮昧,默認(rèn)加載JAVA_HOME/jre/lib/ext/目下的所有jar赋荆。
    3. Application ClassLoader:應(yīng)用類加載器,負(fù)責(zé)加載應(yīng)用程序classpath目錄下的所有jar和class文件懊昨。一般來(lái)說(shuō)窄潭,Java 應(yīng)用的類都是由它來(lái)完成加載的。

父加載器

  • 父加載器不是父類酵颁。 先看下AppClassLoader和ExtClassLoader的繼承關(guān)系嫉你。

    image

    我們來(lái)看下ClassLoader的源碼:

    public abstract class ClassLoader {
      //父加載器
      private final ClassLoader parent;
    
      private static ClassLoader scl;
    
      private ClassLoader(Void unused, ClassLoader parent) {
          this.parent = parent;
          ...
      }
        
      protected ClassLoader(ClassLoader parent) {
          this(checkCreateClassLoader(), parent);
      }
        
      protected ClassLoader() {
          this(checkCreateClassLoader(), getSystemClassLoader());
      }
        
      public final ClassLoader getParent() {
          if (parent == null)
                  return null;
          return parent;
      }
        
      public static ClassLoader getSystemClassLoader() {
          initSystemClassLoader();
          if (scl == null) {
              return null;
          }
          return scl;
      }
    
      private static synchronized void initSystemClassLoader() {
          if (!sclSet) {
              if (scl != null)
                  throw new IllegalStateException("recursive invocation");
              sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
              if (l != null) {
                  Throwable oops = null;
                  //通過(guò)Launcher獲取ClassLoader
                  scl = l.getClassLoader();
                      try {
                      scl = AccessController.doPrivileged(
                          new SystemClassLoaderAction(scl));
                  } catch (PrivilegedActionException pae) {
                      oops = pae.getCause();
                      if (oops instanceof InvocationTargetException) {
                          oops = oops.getCause();
                      }
                  }
                  if (oops != null) {
                      if (oops instanceof Error) {
                          throw (Error) oops;
                      } else {
                          throw new Error(oops);
                      }
                  }
              }
              sclSet = true;
          }
      }
        ...
    }
    

    再來(lái)看看sun.misc.Launcher,它是一個(gè)java虛擬機(jī)的入口:

    public class Launcher {
        
        private static Launcher launcher = new Launcher();
        
        private static String bootClassPath = System.getProperty("sun.boot.class.path");
    
        public static Launcher getLauncher() {
            return launcher;
        }
    
        private ClassLoader loader;
    
        public Launcher() {
            ClassLoader extcl;
            try {
                //初始化ExtClassLoader
                extcl = ExtClassLoader.getExtClassLoader();
            } catch (IOException e) {
                throw new InternalError(
                    "Could not create extension class loader", e);
            }
            try {
                //初始化AppClassLoader
                loader = AppClassLoader.getAppClassLoader(extcl);
            } catch (IOException e) {
                throw new InternalError(
                    "Could not create application class loader", e);
            }
            //設(shè)置AppClassLoader為線程上下文類加載器
            Thread.currentThread().setContextClassLoader(loader);
        }
        
        public ClassLoader getClassLoader() {
            return loader;
        }
       
        static class ExtClassLoader extends URLClassLoader {
          private File[] dirs;
    
            public static ExtClassLoader getExtClassLoader() throws IOException
            {
                final File[] dirs = getExtDirs();
                return new ExtClassLoader(dirs);
            }
    
            public ExtClassLoader(File[] dirs) throws IOException {
                super(getExtURLs(dirs), null, factory);
                this.dirs = dirs;
            }
            ...
        }
    
        static class AppClassLoader extends URLClassLoader {
            public static ClassLoader getAppClassLoader(final ClassLoader extcl)
                throws IOException{
                final String s = System.getProperty("java.class.path");
                final File[] path = (s == null) ? new File[0] : getClassPath(s);
                URL[] urls = (s == null) ? new URL[0] : pathToURLs(path);
                return new AppClassLoader(urls, extcl);
            }
    
            AppClassLoader(URL[] urls, ClassLoader parent) {
                super(urls, parent, factory);
            }
            ...                                                
        }
    }
    

    從以上的源碼中我們可以知道parent的賦值是在ClassLoader對(duì)象的構(gòu)造方法中,它有兩個(gè)情況:

    1. 由外部類創(chuàng)建ClassLoader時(shí)直接傳入一個(gè)ClassLoader為parent材义。

    2. 外界不指定parent時(shí)均抽,由getSystemClassLoader()方法生成,也就是在sun.misc.Laucher通過(guò)getClassLoader()獲取其掂,也就是AppClassLoader油挥。直白的說(shuō),一個(gè)ClassLoader創(chuàng)建時(shí)如果沒(méi)有指定parent款熬,那么它的parent默認(rèn)就是AppClassLoader深寥。

loader = AppClassLoader.getAppClassLoader(extcl);說(shuō)明AppClassLoader的parent是ExtClassLoader。

但是ExtClassLoader并沒(méi)有直接對(duì)parent賦值贤牛。它調(diào)用了它的父類也就是URLClassLoder的構(gòu)造方法并傳遞了3個(gè)參數(shù)惋鹅。

public  URLClassLoader(URL[] urls, ClassLoader parent,URLStreamHandlerFactory factory) {
     super(parent);
}

真相大白,ExtClassLoader的parent為null殉簸。但是實(shí)際上ExtClassLoader父類加載器是BootstrapClassLoader闰集,我們可以從雙親委托中找到蛛絲馬跡。

雙親委托

java 雙親委派.png

? 類加載器在加載類或者其他資源時(shí)般卑,使用的是如上圖所示的雙親委派模型武鲁,這種模型要求除了頂層的BootStrap ClassLoader外,其余的類加載器都應(yīng)當(dāng)有自己的父類加載器蝠检,如果一個(gè)類加載器收到了類加載請(qǐng)求沐鼠,首先會(huì)把這個(gè)請(qǐng)求委派給父類加載器加載,只有父類加載器無(wú)法完成類加載請(qǐng)求時(shí),子類加載器才會(huì)嘗試自己去加載饲梭。要理解雙親委派乘盖,可以查看ClassLoader.loadClass方法。

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        // 檢查是否已經(jīng)加載過(guò)
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            // 沒(méi)有被加載過(guò)
            long t0 = System.nanoTime();
            // 首先委派給父類加載器加載
            try {
                if (parent != null) {
                     //父加載器不為空則調(diào)用父加載器的loadClass
                    c = parent.loadClass(name,false);
                } else {
                     //父加載器為空則調(diào)用Bootstrap Classloader
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                // 如果父類加載器無(wú)法加載憔涉,才嘗試加載
                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;
    }
}

? 在雙親委托把類加載事件一直往上傳遞订框,一直傳到ExtClassLoader,由于ExtClassLoader中的parent為null而傳給BootStrapClassLoader兜叨。所以說(shuō)ExtClassLoader的父加載器為BootStrapClassLoader布蔗。之所以ExtClassLoader不持有BootStrapClassLoader的引用,是因?yàn)锽ootstrap ClassLoader是由C/C++編寫的浪腐,它本身是虛擬機(jī)的一部分,所以它并不是一個(gè)JAVA類顿乒,也就是無(wú)法在java代d碼中獲取它的引用议街。

  • 優(yōu)點(diǎn):通過(guò)雙親委托可以避免重復(fù)加載和保證安全性。當(dāng)父親已經(jīng)加載了該類的時(shí)候璧榄,就沒(méi)有必要子ClassLoader再加載一次特漩。如果我們自定義一個(gè)String來(lái)動(dòng)態(tài)替換java核心api中定義的類型,這樣會(huì)存在非常大的安全隱患骨杂,而雙親委托的方式涂身,就可以避免這種情況,因?yàn)镾tring已經(jīng)在啟動(dòng)時(shí)就被引導(dǎo)類加載器(Bootstrcp ClassLoader)加載搓蚪,所以永遠(yuǎn)也無(wú)法加載一個(gè)自己寫的String蛤售,除非你改變JDK中ClassLoader搜索類的默認(rèn)算法。

參考

? 深入分析Java ClassLoader原理

? 一看你就懂妒潭,超詳細(xì)java中的ClassLoader詳解

? 深入理解JVM之ClassLoader

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末悴能,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子雳灾,更是在濱河造成了極大的恐慌漠酿,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谎亩,死亡現(xiàn)場(chǎng)離奇詭異炒嘲,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)匈庭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門夫凸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人嚎花,你說(shuō)我怎么就攤上這事寸痢。” “怎么了紊选?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵啼止,是天一觀的道長(zhǎng)道逗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)献烦,這世上最難降的妖魔是什么滓窍? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮巩那,結(jié)果婚禮上吏夯,老公的妹妹穿的比我還像新娘。我一直安慰自己即横,他們只是感情好噪生,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著东囚,像睡著了一般跺嗽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上页藻,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天桨嫁,我揣著相機(jī)與錄音,去河邊找鬼份帐。 笑死璃吧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的废境。 我是一名探鬼主播畜挨,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼彬坏!你這毒婦竟也來(lái)了朦促?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤栓始,失蹤者是張志新(化名)和其女友劉穎务冕,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幻赚,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡禀忆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了落恼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片箩退。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖佳谦,靈堂內(nèi)的尸體忽然破棺而出戴涝,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布啥刻,位于F島的核電站奸鸯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏可帽。R本人自食惡果不足惜娄涩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望映跟。 院中可真熱鬧蓄拣,春花似錦、人聲如沸努隙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)荸镊。三九已至碎捺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贷洲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工晋柱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留优构,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓雁竞,卻偏偏與公主長(zhǎng)得像钦椭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子碑诉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • ClassLoader翻譯過(guò)來(lái)就是類加載器彪腔,普通的java開發(fā)者其實(shí)用到的不多,但對(duì)于某些框架開發(fā)者來(lái)說(shuō)卻非常常見(jiàn)...
    時(shí)待吾閱讀 1,073評(píng)論 0 1
  • 1 基本信息 每個(gè)開發(fā)人員對(duì)java.lang.ClassNotFoundExcetpion這個(gè)異辰裕肯定都不陌生德挣,...
    java小菜鳥閱讀 2,608評(píng)論 0 15
  • 作者簡(jiǎn)介 原創(chuàng)微信公眾號(hào)郭霖 WeChat ID: guolin_blog 本篇是fank909的第四篇投稿格嗅,詳細(xì)...
    木木00閱讀 1,607評(píng)論 1 14
  • 麥子在大學(xué)里面做了兩件引起轟動(dòng)的事情:拿著水果刀追著渣男跑;在食堂面前當(dāng)著所有人的面唠帝,狠狠扇了她前男友一巴掌屯掖。 這...
    葉況閱讀 711評(píng)論 1 11
  • 1 類委托 Derived 的超類型列表中的 by句表示b 將會(huì)在 Derived 中內(nèi)部存儲(chǔ)。 并且編譯器將成轉(zhuǎn)...
    NiceDream閱讀 1,535評(píng)論 1 3