服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

classloader介绍

 


jvm在运行时会产生三个classloader,bootstrap classloader、extension classloader和appclassloader.其中,bootstrap是用c++编写的,我们在java中看不到它,是null。它用来加载核心类库,在jvm源代码中这样写道:
static const char classpathformat[] =
"%/lib/rt.jar:"
"%/lib/i18n.jar:"
"%/lib/sunrsasign.jar:"
"%/lib/jsse.jar:"
"%/lib/jce.jar:"
"%/lib/charsets.jar:"
"%/classes";
知道为什么不需要在classpath中加载这些类了吧?人家在jvm启动的时候就自动加载了,并且在运行过程中根本不能修改bootstrap加载路径。extension classloader用来加载扩展类,即/lib/ext中的类。最后appclassloader才是加载classpath的。classloader加载类用的是委托模型。即先让parent类(而不是super,不是继承关系)寻找,parent找不到才自己找。看来classloader还是蛮孝顺的。三者的关系为:appclassloader的parent是extclassloader,而extclassloader的parent为bootstrap classloader。加载一个类时,首先bootstrap先进行寻找,找不到再由extclassloader寻找,最后才是appclassloader。
为什么要设计的这么复杂呢?其中一个重要原因就是安全性。比如在applet中,如果编写了一个java.lang.string类并具有破坏性。假如不采用这种委托机制,就会将这个具有破坏性的string加载到了用户机器上,导致破坏用户安全。但采用这种委托机制则不会出现这种情况。因为要加载java.lang.string类时,系统最终会由bootstrap进行加载,这个具有破坏性的string永远没有机会加载。
我们来看这段代码:
//a.java
public class a{
public static void main(string[] args){
a a=new a();
system.out.println(system.getproperty("java.ext.dirs"));
system.out.println(a.getclass().getclassloader());
b b=new b();
b.print();
}
}
//b.java
public class b{
public void print(){
system.out.println(this.getclass().getclassloader());
}
}
1、我们将它放在classpath中,则打印出
sun.misc.launcher$appclassloader@92e78c
sun.misc.launcher$appclassloader@92e78c
可见都是由appclassloader来加载的。
2、我们将其放在%jre%/lib/ext/classes(即extclassloader的加载目录。其加载/lib/ext中的jar文件或者子目录classes中的class文件)中。则会打印出:
sun.misc.launcher$extclassloader
sun.misc.launcher$extclassloader
3、我们将a.class放到%jre%/lib/ext/classes中,而将b.class放到classpaht中又会怎么样呢?结果是:
sun.misc.launcher$extclassloader
exception in thread "main" java.lang.noclassdeffounderror:b
at a.main(a.java:6)
怎么会这样呢?这其中有一个重要的问题:a类当然是由extclassloader来加载的,b类要由哪个加载呢?b类要由调用它自己的类的类加载器(真拗口)。也就是说,a调用了b,所以b由a的类加载器extclassloader来加载。extclassloader根据委托机制,先拜托bootstrap加载,bootstrap没有找到。然后它再自己寻找b类,还是没找到,所以抛出异常。extclassloader不会请求appclassloader来加载!你可能会想:这算什么问题,我把两个类放到一起不就行了?呵呵,没这么简单。比如jdbc是核心类库,而各个数据库的jdbc驱动则是扩展类库或在classpath中定义的。所以jdbc由bootstrap classloader加载,而驱动要由appclassloader加载。等等,问题来了,bootstrap不会请求appclassloader加载类啊。那么,他们怎么实现的呢?我就涉及到一个context classloader的问题,调用thread.getcontextclassloader。

扫描关注微信公众号