2008-04-07
CLASSLOADER与类的依赖关系
起因:为了排查一个生产环境的问题,需要打印特殊的调试日志。能否做到通过配置,调试的类优先于原有类的载入,回退的时候修改配置即可?
一般的情况下:可以将 CLASSPATH设置成:/opt/lib/debug.jar:/opt/lib/c.jar的时候debug.jar的类优先于c.jar的载入,调试类生效和打印调试日志。CLASSPATH设置成:/opt/lib/c.jar则还原回正式的类。
在多层ClassLoader的情况下,调试类放在ClassPath路径中,c.jar放在子ClassLoader加载路径中,却出了“意外”。为了说明情况,做了一个测试项目。系统载入结构如下:
被载入类Container和Item,Item是Container的成员类
载入类为StartServer,其关键的载入代码如下:
测试结果:
1、Class Item放在AppClassLoader载入,Class Container放在MYClassLoader载入 上述载入正常运行。
E:\javaproject\ws2007\tempProject\bin>java -Dload_dir=E:/javaproject/ws2007/tempProject -cp . StartServer
***************************
parent is:class sun.misc.Launcher$AppClassLoader
file:/E:/javaproject/ws2007/tempProject/c.jar
Container:MYClassLoader@61de33
Item:sun.misc.Launcher$AppClassLoader@19821f
测试结果:2、Class Container放在AppClassLoader载入,Class Item放在MYClassLoader载入 出现NoClassDefFoundError错误
E:\javaproject\ws2007\tempProject\bin>java -Dload_dir=E:/javaproject/ws2007/tempProject -cp . StartServer
***************************
parent is:class sun.misc.Launcher$AppClassLoader
file:/E:/javaproject/ws2007/tempProject/c.jar
Container:sun.misc.Launcher$AppClassLoader@19821f
Exception in thread "main" java.lang.NoClassDefFoundError: Item
at Container.<init>(Container.java:6)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at java.lang.Class.newInstance0(Class.java:355)
at java.lang.Class.newInstance(Class.java:308)
at StartServer.start(StartServer.java:48)
at StartServer.main(StartServer.java:18)
分析:
根据大名鼎鼎的类加载委托规则,ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。
由于子ClassLoader含有父ClassLoader的引用,并且可以将委托父ClassLoader搜索类和资源,反之则不行。于是在上述第二个测试中,MYClassLoader被显式加载Container,MYClassLoader委托AppClassLoader进行加载该类,AppClassLoader加载Container的时候发现无法加载Item类,其父ClassLoader是ExtClassLoader和Bootstrap ClassLoader当然也无法加载Item类, 因为Item仅出现在MYClassLoader的加载路径中,因而出错。
结论:
父ClassLoader与子ClassLoader加载的类必须有正确的依赖关系,子ClassLoader加载的类可以依赖饮用父ClassLoader加载的类,反之不行,如果需要将一个类移动上父ClassLoader加载则需要将该类依赖的所有类移到同一ClassLoader或者移动到更高层的ClassLoader。
另外,在测试过程中,中了-jar的招,以此纪念。一个可执行的 JAR 必须通过 menifest 文件的头引用它所需要的所有其他从属 JAR。如果使用了 -jar 选项,那么环境变量 CLASSPATH 和在命令行中指定的所有类路径都被 JVM 所忽略。 (http://www-128.ibm.com/developerworks/cn/java/j-jar/index.html)
一般的情况下:可以将 CLASSPATH设置成:/opt/lib/debug.jar:/opt/lib/c.jar的时候debug.jar的类优先于c.jar的载入,调试类生效和打印调试日志。CLASSPATH设置成:/opt/lib/c.jar则还原回正式的类。
在多层ClassLoader的情况下,调试类放在ClassPath路径中,c.jar放在子ClassLoader加载路径中,却出了“意外”。为了说明情况,做了一个测试项目。系统载入结构如下:
被载入类Container和Item,Item是Container的成员类
public class Container {
private Item item=null;
public Container(){
println();
this.item=new Item();
}
public void println(){
System.out.println("Container:"+this.getClass().getClassLoader());
}
}
public class Item {
public Item(){
println();
}
public void println(){
System.out.println("Item:"+this.getClass().getClassLoader());
}
}
载入类为StartServer,其关键的载入代码如下:
private void start() {
try {
System.out.println("***************************");
// Load up the bootstrap container
final ClassLoader parent = findParentClassLoader();
String libDirString = System.getProperty("load_dir");
File libDir;
if (libDirString != null) {
libDir = new File(libDirString);
if (!libDir.exists()) {
throw new NullPointerException("dir not exit");
}
}
else {
throw new NullPointerException("dir not exit");
}
System.out.println("parent is:"+parent.getClass());
ClassLoader loader = new MYClassLoader(parent, libDir);
Thread.currentThread().setContextClassLoader(loader);
Class containerClass = loader.loadClass(
"Container");
containerClass.newInstance();
}
catch (Exception e) {
e.printStackTrace();
}
}
测试结果:
1、Class Item放在AppClassLoader载入,Class Container放在MYClassLoader载入 上述载入正常运行。
E:\javaproject\ws2007\tempProject\bin>java -Dload_dir=E:/javaproject/ws2007/tempProject -cp . StartServer
***************************
parent is:class sun.misc.Launcher$AppClassLoader
file:/E:/javaproject/ws2007/tempProject/c.jar
Container:MYClassLoader@61de33
Item:sun.misc.Launcher$AppClassLoader@19821f
测试结果:2、Class Container放在AppClassLoader载入,Class Item放在MYClassLoader载入 出现NoClassDefFoundError错误
E:\javaproject\ws2007\tempProject\bin>java -Dload_dir=E:/javaproject/ws2007/tempProject -cp . StartServer
***************************
parent is:class sun.misc.Launcher$AppClassLoader
file:/E:/javaproject/ws2007/tempProject/c.jar
Container:sun.misc.Launcher$AppClassLoader@19821f
Exception in thread "main" java.lang.NoClassDefFoundError: Item
at Container.<init>(Container.java:6)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at java.lang.Class.newInstance0(Class.java:355)
at java.lang.Class.newInstance(Class.java:308)
at StartServer.start(StartServer.java:48)
at StartServer.main(StartServer.java:18)
分析:
根据大名鼎鼎的类加载委托规则,ClassLoader 类使用委托模型来搜索类和资源。每个 ClassLoader 实例都有一个相关的父类加载器。需要查找类或资源时,ClassLoader 实例会在试图亲自查找类或资源之前,将搜索类或资源的任务委托给其父类加载器。虚拟机的内置类加载器(称为 "bootstrap class loader")本身没有父类加载器,但是可以将它用作 ClassLoader 实例的父类加载器。
由于子ClassLoader含有父ClassLoader的引用,并且可以将委托父ClassLoader搜索类和资源,反之则不行。于是在上述第二个测试中,MYClassLoader被显式加载Container,MYClassLoader委托AppClassLoader进行加载该类,AppClassLoader加载Container的时候发现无法加载Item类,其父ClassLoader是ExtClassLoader和Bootstrap ClassLoader当然也无法加载Item类, 因为Item仅出现在MYClassLoader的加载路径中,因而出错。
结论:
父ClassLoader与子ClassLoader加载的类必须有正确的依赖关系,子ClassLoader加载的类可以依赖饮用父ClassLoader加载的类,反之不行,如果需要将一个类移动上父ClassLoader加载则需要将该类依赖的所有类移到同一ClassLoader或者移动到更高层的ClassLoader。
另外,在测试过程中,中了-jar的招,以此纪念。一个可执行的 JAR 必须通过 menifest 文件的头引用它所需要的所有其他从属 JAR。如果使用了 -jar 选项,那么环境变量 CLASSPATH 和在命令行中指定的所有类路径都被 JVM 所忽略。 (http://www-128.ibm.com/developerworks/cn/java/j-jar/index.html)
发表评论
提醒: 该博客已发表在公共论坛,博客所有留言会成为论坛回贴,留言请注意遵守论坛发贴规则
- 浏览: 19063 次
- 性别:

- 来自: 0

- 详细资料
搜索本博客
我的相册
doc
共 20 张
共 20 张
最近加入圈子
最新评论
-
又见内存泄露
说的很好,以后有个解决问题的参考了:)
-- by lean1252 -
一种SSO的实现方案
登录信息的管理中心一旦建立,就会造成所有的系统都对这个管理中心产生依赖。如果这个 ...
-- by downpour -
一种SSO的实现方案
有很多所谓的登陆的话,就是从统一登录系统中想办法把用户信息传递到另外的系统。这似 ...
-- by 香克斯 -
又见内存泄露
很是精髓,但是没有做运维,还不是很懂。
-- by zpple -
SAAS在电子商务中的应用分 ...
淘宝模式不更好?
-- by rtdb






评论排行榜