JAVA类的加载(4) ——类之间能够隔离&类占用的资源能回收

一、类加载体系

类加载方式:代理模式 或 双亲委托

例1:

 1 package classloader.system;
 2 
 3 public class Example {
 4     public static void main(String[] args) {
 5         /*应用的类加载器是AppClassLoader,首先委托父ClassLoder(ExtClassLoder)从他自己的资源池中(jre/lib/ext)找这个类,
 6          * 在这之前,ExtClassLoder又委托父ClassLoader(BootStrapClassLoader)从jre/lib找这个类;
 7          * BootStrapClassLoader在jre/lib没找到需要的类,返回到ExtClassLoder;ExtClassLoder在jre/lib/ext没找到需要的类,返回到AppClassLoader
 8          *AppClassLoader从自己的资源池中查找 c = findClass(name);
 9          * 
10          */
11         ClassLoader cl = Example.class.getClassLoader();  
12         while (cl != null) {
13             System.out.println(cl.toString());
14             cl = cl.getParent();
15         }
16     }
17 }

结果:

sun.misc.Launcher$AppClassLoader@425224ee
sun.misc.Launcher$ExtClassLoader@1ef6a746

例2:在eclipse中点击例1中的11行ClassLoader,进入ClassLoader类的代码,找到loadClass方法,里面就是详细的类加载代码

 1  protected synchronized Class<?> loadClass(String name, boolean resolve)
 2     throws ClassNotFoundException
 3     {
 4     // First, check if the class has already been loaded
 5     Class c = findLoadedClass(name);
 6     if (c == null) {
 7         try {
 8         if (parent != null) {
 9             c = parent.loadClass(name, false); //递归通过父加载器加载
10         } else {
11             c = findBootstrapClassOrNull(name); //从根加载器的资源池中查找这个类
12         }
13         } catch (ClassNotFoundException e) {
14                 // ClassNotFoundException thrown if class not found
15                 // from the non-null parent class loader
16             }
17             if (c == null) {
18             // If still not found, then invoke findClass in order
19             // to find the class.
20             c = findClass(name); //应用加载器从自己的资源池中查找这个类
21         }
22     }
23     if (resolve) {
24         resolveClass(c);
25     }
26     return c;
27     }

 

为什么要这么做呢?(即为什么要用 代理模式 或 双亲委托)
安全考虑:直接在AppClassLoader写一个String这样的类,如果不是委托父类加载,这个类可能性能不高、有问题,导致加载类的时候出现一些意想不到的错误   通过这样的委托模式,使得JDK自己的类能够优先加载,保证这些类的安全等
 
注意:
ExtClassLoader  ——这个加载器对应的资源池(目录)建议一般不要改,比如jboss的数据库加密安全算法在这里面
自定义的classLoader——配置相关的加载地方
 
二、自定义的classLoader配置相关的资源池(从什么地方加载相关类),模拟了从classpath之外的地方加载类
 
例3:Example.java
 1 package classloader.system2;
 2 
 3 import java.lang.reflect.Method;
 4 import java.net.MalformedURLException;
 5 import java.net.URL;
 6 import java.net.URLClassLoader;
 7 import java.io.File;
 8 
 9 public class Example {
10     public static void main(String[] args) throws Exception {
11 /*前面假设是一个web容器,web容器通过前面的方式启动起来了;当你扔一个war包进去,war包有自己的ClassLoader以及自己的url,url指向war包里面的lib目录
12  * 相当于可以从url里面加载类       用web应用的classloader去加载相关的类,并且调它的初始化方法,就可以让我自己的war包运行起来
13  * war包里面都有一个web.xml,web.xml描述了Servlet,相当于描述了全称类名叫什么,我就可以启动相关的Servlet;相关的Servlet启动以后,我就可以接受请求并进行分发,web容器就基本上运行起来了
14  * 
15  * 
16  */
17         URL url = new File(args[0]).toURL();
18         ClassLoader cl = new URLClassLoader(new URL[]{url});
19         Class<?> tvClass = cl.loadClass("classloader.system2.Television");
20         Object tv = tvClass.newInstance();  //因为我们当前classpath下是没有Television类的,所以这里定义为Object tv
21         
22         Object panel = cl.loadClass("classloader.system2.Panel").newInstance();  //load一个面板并实例化
23         Method setPanelMethod = tvClass.getMethod("setPanel", Object.class);
24         setPanelMethod.invoke(tv, panel);   
25         Method playVideoMethod = tvClass.getMethod("playVideo", new Class[]{});
26         playVideoMethod.invoke(tv, new Object[]{});
27         
28         System.out.println("不同的ClassLoader加载。。。");
29         ClassLoader cl2 = new URLClassLoader(new URL[]{url});
30         Object panel2 = cl2.loadClass("classloader.system2.Panel").newInstance();
31         setPanelMethod.invoke(tv, panel2);  //tv是cl加载的,panel2是cl2加载的,两个不同的classload,它们之间的对象是不能直接进行装配的,会认为不一样的
32     }
33 }
34 
35 //将Panel和Televison两个类拷贝到特定目录并编译,并删除当前classpath下的两个类

//Television.java

 1 package classloader.system2;
 2 
 3 public class Television {
 4     private Panel panel;  //面板
 5     
 6     public void playVideo() {
 7         panel.display();
 8     }
 9     
10     public void setPanel(Object panel) {
11         this.panel = (Panel)panel;
12     }
13 }

//Panel.java

1 package classloader.system2;
2 
3 public class Panel {
4     public void display() {
5         System.out.println("In Panel: display()");
6     }
7 }


运行:

编译好后,将Panel和Televison两个类class文件拷贝到特定目录(如下),并删除当前classpath下的两个类(main的参数String[] args为d:\tmp



1、ClassLoader cl = new URLClassLoader(new URL[]{url}); 这个classLoader所指向的url在d:\,d:\下Class<?> tvClass = cl.loadClass("classloader.system2.Television");时找不到,所以就报错了



2、填写正确的路径



 classloader.system2.Panel cannot be cast to classloader.system2.Panel报错原因如下:
tv是cl加载的,panel2是cl2加载的,两个不同的classload,它们之间的对象是不能直接进行装配的,会认为不一样的


对象与类的加载 小结:
1、并非所有的类都需要在CLASSPATH中
2、对象的赋值与转换与其ClassLoader相关
3、类在一个ClassLoader中只加载一次

三、与类加载相关的异常

1、ClassNotFoundException
2、NoClassDefFoundError
 1 package classloader.exception;
 2 
 3 
 4 public class Example {
 5     public static void main(String[] args) throws Exception {
 6         Television tv = (Television) Example.class.getClassLoader()
 7                 .loadClass("classloader.exception.Television").newInstance(); //loadClass("classloader.exception.Television1") 就会抛java.lang.ClassNotFoundException:
 8                                                                               //exception
 9         tv.playVideo();
10     }
11 }
12 
13 class Television {
14     //private Panel panel; 会报错at classloader.exception.Television.playVideo(Example.java:17)
15     private Panel panel = new Panel();  //Televison编译强依赖于Panel,如果编译好后再删除Panel.class,运行时会报java.lang.NoClassDefFoundError: classloader/exception/Panel
16                                          //error
17     public void playVideo() {
18         panel.display();
19     }
20     
21     public void setPanel(Object panel) {
22         this.panel = (Panel)panel;
23     }
24 }
25 
26 
27 class Panel {
28     public void display() {
29         System.out.println("In Panel: display()");
30     }
31 }

 

四、类的回收
1、ClassLoader加载类,类实例化对象
2、当某个ClassLoader加载的所有类实例化的所有对象都被回收了,则该CL会被回收

类对象实例化业务对象

 web容器放了个war包,当war包被应用服务器加载起来后,应用服务器会为这个war包分配classloader,classloader负责加载这个war包里面的

所有资源,加载完了后开始运行,直到某一时刻,我们觉得这些所有对象都不需要了,里面的所有资源要被回收掉,如Servlet要被销毁等一系列步骤;

当这些类加载出来的对象都被回收后,这个classloader就可以被回收掉了


classloaderA   A1

classloaderB   B1   B1持有A1对象,classloaderA不能被回收


五、类的热替换思路



参考:
 

posted on 2013-11-13 11:51  gogoy  阅读(1240)  评论(0编辑  收藏  举报

导航