ClassLoader 加載機制

          1. Java Class Loading Mechanism

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

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

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

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

          ClassLoader工作原理:

          了解ClassLoader工作原理,先來看一個ClassLoader類簡化版的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是否已被加載,如果已被加載則直接返回,否則調用parent的loadClass來加載,如果parent是null代表是Bootstrap ClassLoader,則有Bootstrap ClassLoader來加載,如果都未加載成功,最后由該ClassLoader自己加載。這種parent-child委派模型,保證了惡意的替換Java核心類不會發(fā)生,因為如果定義了一個惡意java.lang.String,它首先會被JVM的Bootstrap ClassLoader加載自己JRE/lib下的,而不會加載惡意的。另外,Java允許同一package下的類可以訪問受保護成員的訪問權限,如定義一個java.lang.Bad,但是因為java.lang.String由Bootstrap ClassLoader加載而java.lang.Bad由AppClassLoader加載,不是同一ClassLoader加載,仍不能訪問。

          2. Hotswap - 熱部署

          即不重啟JVM,直接替換class。因為ClassLoader特性,同一個ClassLoader命名空間不能加載兩個同名的類,所以在不重啟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方法實現(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轉型為IExample接口而不是Exmaple,否則會拋出ClassCastExecption,這是因為swap方法所在類Hotswap是有AppClassLoader加載的,而且加載Hotswap的同時會加載該類引用的Exmaple的symbol link,而Example是MyClassLoader加載的,不同的ClassLoader加載的類之間直接用會拋出ClassCastException, 在本例中ClassLoader實現(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 }
          而對IExample我們還是調用super的loadClass方法,該方法實現(xiàn)仍是JVM的parent-child委派方式,因此最終由AppClassLoader加載,加載Hotswap時加載的symbol link也是由AppClassLoader加載的,因此能夠成功。

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

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

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

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


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


          網站導航:
           
          <2012年9月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456

          導航

          統(tǒng)計

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 儋州市| 武安市| 大厂| 菏泽市| 广汉市| 新宁县| 安福县| 墨竹工卡县| 南昌县| 瓮安县| 浪卡子县| 通许县| 靖远县| 开封市| 岳阳县| 茶陵县| 尉犁县| 个旧市| 巩留县| 山阳县| 定边县| 西充县| 吉安市| 昌平区| 垣曲县| 房山区| 嵩明县| 祥云县| 怀宁县| 灌云县| 读书| 从江县| 清徐县| 安国市| 涟源市| 额敏县| 乌拉特中旗| 长治县| 彰武县| 荣昌县| 周至县|