ClassLoader 加載機(jī)制

          1. Java Class Loading Mechanism

          首先當(dāng)編譯一個(gè)Java文件時(shí),編譯器就會(huì)在生成的字節(jié)碼中內(nèi)置一個(gè)public,static,final的class字段,該字段屬于java.lang.Class類型,該class字段使用點(diǎn)來訪問,所以可以有:
          java.lang.Class clazz = MyClass.class

          當(dāng)class被JVM加載,就不再加載相同的class。class在JVM中通過(ClassLoader,Package,ClassName)來唯一決定。ClassLoader指定了一個(gè)class的scope,這意味著如果兩個(gè)相同的包下面的class被不同的ClassLoader加載,它們是不一樣的,并且不是type-compatible的。

          JVM中所有的ClassLoader(bootstrap ClassLoader除外)都是直接或間接繼承于java.lang.ClassLoader抽象類,并且人為邏輯上指定了parent-child關(guān)系,實(shí)現(xiàn)上child不一定繼承于parent,我們也可以通過繼承它來實(shí)現(xiàn)自己的ClassLoader。

          JVM ClassLoder架構(gòu),從上到下依次為parent-child關(guān)系:
          • Bootstrap ClassLoader - 啟動(dòng)類加載器,主要負(fù)責(zé)加載核心Java類如java.lang.Object和其他運(yùn)行時(shí)所需class,位于JRE/lib目錄下或-Xbootclasspath指定的目錄。我們不知道過多的關(guān)于Bootstrap ClassLoader的細(xì)節(jié),因?yàn)樗且粋€(gè)native的實(shí)現(xiàn),不是Java實(shí)現(xiàn),所以不同JVMs的Bootstrap ClassLoader的行為也不盡相同。調(diào)用java.lang.String.getClassLoder() 返回null。
          • sun.misc.ExtClassLoader - 擴(kuò)展類加載器,負(fù)責(zé)加載JRE/lib/ext目錄及-Djava.ext.dirs指定目錄。
          • sun.misc.AppClassLoader - 應(yīng)用類加載器,負(fù)責(zé)加載java.class.path目錄
          • 另外,還有一些其他的ClassLoader如:java.net.URLClassLoader,java.security.SecureClassLoader,java.rmi.server.RMIClassLoader,sun.applet.AppletClassLoader
          • 用戶還可以自己繼承java.lang.ClassLoader來實(shí)現(xiàn)自己的ClassLoader,用來動(dòng)態(tài)加載class文件。
          ClassLoader特性
          • 每個(gè)ClassLoader維護(hù)一份自己的命名空間,同一個(gè)ClassLoader命名空間不能加載兩個(gè)同名的類。
          • 為實(shí)現(xiàn)Java安全沙箱模型,默認(rèn)采用parent-child加載鏈結(jié)構(gòu),除Bootstrap ClassLoader沒有parent外,每個(gè)ClassLoader都有一個(gè)邏輯上的parent,就是加載這個(gè)ClassLoader的ClassLoader,因?yàn)镃lassLoader本身也是一個(gè)類,直接或間接的繼承java.lang.ClassLoader抽象類。
          java.lang.Thread中包含一個(gè)public的方法public ClassLoader getContextClassLoader(),它返回某一線程相關(guān)的ClassLoader,該ClassLoader是線程的創(chuàng)建者提供的用來加載線程中運(yùn)行的classes和資源的。如果沒有顯式的設(shè)置其ClassLoader,默認(rèn)是parent線程的Context ClassLoader。Java默認(rèn)的線程上下文加載器是AppClassLoader。

          ClassLoader工作原理:

          了解ClassLoader工作原理,先來看一個(gè)ClassLoader類簡(jiǎn)化版的loadClass()方法源碼
           1 protected Class<?> loadClass(String name, boolean resolve)
           2         throws ClassNotFoundException
           3     {
           4         synchronized (getClassLoadingLock(name)) {
           5             // First, check if the class has already been loaded
           6             Class c = findLoadedClass(name);
           7             if (c == null) {
           8                 long t0 = System.nanoTime();
           9                 try {
          10                     if (parent != null) {
          11                         c = parent.loadClass(name, false);
          12                     } else {
          13                         c = findBootstrapClassOrNull(name);
          14                     }
          15                 } catch (ClassNotFoundException e) {
          16                     // ClassNotFoundException thrown if class not found
          17                     // from the non-null parent class loader
          18                 }
          19 
          20                 if (c == null) {
          21                     // If still not found, then invoke findClass in order
          22                     // to find the class.
          24                     c = findClass(name);
          25                 }
          26             }
          27             if (resolve) {
          28                 resolveClass(c);
          29             }
          30             return c;
          31         }
          32     }

          首先查看該class是否已被加載,如果已被加載則直接返回,否則調(diào)用parent的loadClass來加載,如果parent是null代表是Bootstrap ClassLoader,則有Bootstrap ClassLoader來加載,如果都未加載成功,最后由該ClassLoader自己加載。這種parent-child委派模型,保證了惡意的替換Java核心類不會(huì)發(fā)生,因?yàn)槿绻x了一個(gè)惡意java.lang.String,它首先會(huì)被JVM的Bootstrap ClassLoader加載自己JRE/lib下的,而不會(huì)加載惡意的。另外,Java允許同一package下的類可以訪問受保護(hù)成員的訪問權(quán)限,如定義一個(gè)java.lang.Bad,但是因?yàn)閖ava.lang.String由Bootstrap ClassLoader加載而java.lang.Bad由AppClassLoader加載,不是同一ClassLoader加載,仍不能訪問。

          2. Hotswap - 熱部署

          即不重啟JVM,直接替換class。因?yàn)镃lassLoader特性,同一個(gè)ClassLoader命名空間不能加載兩個(gè)同名的類,所以在不重啟JVM的情況下,只能通過新的ClassLoader來重新load新的class。

           1  public static void main(String[] args) throws InterruptedException, MalformedURLException {
           2         IExample oldExample = new Example();
           3         oldExample.plus();
           4         System.out.println(oldExample.getCount());
           5 
           6         Hotswap hotswap = new Hotswap();
           7         while (true) {
           8             IExample newExample = hotswap.swap(oldExample);
           9             String message = newExample.message();
          10             int count = newExample.plus();
          11             System.out.println(message.concat(" : " + count));
          12             oldExample = newExample;
          13             Thread.sleep(5000);
          14         }
          15     }
          16 
          利用hotswap替換就的Example,每5秒鐘輪詢一次,swap方法實(shí)現(xiàn)如下:
           1  private IExample swap(IExample old) {
           2         try {
           3             String sourceFile = srcPath().concat("Example.java");
           4             if (isChanged(sourceFile)) {
           5                 comiple(sourceFile, classPath());
           6                 MyClassLoader classLoader = new MyClassLoader(new URL[]{new URL("file:"+classPath())});
           7                 Class<?> clazz = classLoader.loadClass("Example");
           8                 System.out.println(IExample.class.getClassLoader());
           9                 IExample exampleInstance = ((IExample) clazz.newInstance()).copy(old);
          10                 System.out.println(exampleInstance.getClass().getClassLoader());
          11                 return exampleInstance;
          12             }
          13         } catch ...
          24         return old;
          25     }
          這里必須將exampleInstance轉(zhuǎn)型為IExample接口而不是Exmaple,否則會(huì)拋出ClassCastExecption,這是因?yàn)閟wap方法所在類Hotswap是有AppClassLoader加載的,而且加載Hotswap的同時(shí)會(huì)加載該類引用的Exmaple的symbol link,而Example是MyClassLoader加載的,不同的ClassLoader加載的類之間直接用會(huì)拋出ClassCastException, 在本例中ClassLoader實(shí)現(xiàn)如下:
           1 public class MyClassLoader extends URLClassLoader {
           2 
           3     public MyClassLoader(URL[] urls) {
           4         super(urls);
           5     }
           6 
           7     @Override
           8     public Class<?> loadClass(String name) throws ClassNotFoundException {
           9         if ("Example".equals(name)) {
          10             return findClass(name);
          11         }
          12         return super.loadClass(name);
          13     }
          14 }
          而對(duì)IExample我們還是調(diào)用super的loadClass方法,該方法實(shí)現(xiàn)仍是JVM的parent-child委派方式,因此最終由AppClassLoader加載,加載Hotswap時(shí)加載的symbol link也是由AppClassLoader加載的,因此能夠成功。

          此外再熱部署時(shí),被替換的類的所有引用及狀態(tài)都要遷移到新的類上,本例中只是很簡(jiǎn)單的調(diào)用copy函數(shù)遷移了count的狀態(tài)。

          Tomcat的jsp熱部署機(jī)制就是基于ClassLoader實(shí)現(xiàn)的,對(duì)于其類的熱部署機(jī)制是通過修改內(nèi)存中的class字節(jié)碼實(shí)現(xiàn)的。

          Resource:
          Reloading Java Classes 101: Objects, Classes and ClassLoaders
          Internals of Java Class Loading

          posted on 2012-09-08 17:58 *** 閱讀(635) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2012年9月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 醴陵市| 义乌市| 黄骅市| 东辽县| 贵州省| 祁阳县| 文水县| 中西区| 壤塘县| 道真| 双鸭山市| 沾化县| 西藏| 潞西市| 壤塘县| 惠来县| 福贡县| 阿坝县| 宁阳县| 寿宁县| 浦东新区| 淮安市| 军事| 个旧市| 惠州市| 桐庐县| 武威市| 河池市| 拜泉县| 五指山市| 宁化县| 岑巩县| 无极县| 新野县| 文山县| 喀喇沁旗| 玛多县| 长兴县| 余江县| 天峨县| 瓮安县|