分享

类加载器机器委托机制的深入

 I_T_馆 2014-07-26
一   类加载器,和类加载器的作用

有一个类在classpath目录下,需要把它加载到内存中来进行一些处理,处理完的结果就是一些字节码.那是谁吧这些class类加载到内存中来的呢?就是类加载器.

二   java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类加载器负责加载不同位置的类:BookStrap,ExtClassLoader,AppClassLoader

三   类加载器本身也是一个java类,因为类加载器本身也是一个java类,那么这个特殊的java类是有谁加载进来的呢?这显然要有第一个类加载器,这第一个类加载器不是一个java类,他是BookStrap。


BootStrap不是一个java类,不需要类加载器java加载,他是嵌套在java虚拟机内核里面的。java 虚拟机内核已启动的时候,他就已经在那里面了,他是用c++语言写的一段二进制代码。他可以去加载别的类,其中别的类就包含了类加载器

下面我们看一个例子:
 获取ClassLoaderTest这个类的类加载器的名字
 
  
 
我们看到输入结果:


第一个类ClassLoaderTest的类加载器的名称是AppClassLoader。也就是这个类是由AppClassLoader这个类加载器加载的。

第二个类System的类加载器是null。这说明这个类加载器是由BootStrap加载的。因为我们上面说了BootStrap不是java类,不需要类加载器加载。所以他的类加载器是null。

==================================================================

我们说了java给我们提供了三种类加载器:BootStrap,ExtClassLoader,AppClassLoader。这三种类加载器是有父子关系组成了一个树形结构。BootStrap是根节点,BootStrap下面挂着ExtClassLoader,ExtClassLoader下面挂着AppClassLoader。

下面我们用程序来看看他们三个的关系:

 打印的结果:
 通过这段程序可以看出来,ClassLoaderTest由AppClassLoader加载,AppClassLoader的父类节点是ExtClassLoader,ExtClassLoader的父节点是BootStrap。

 
每一个类加载器都有自己的管辖范围。 BootStrap根节点,只负责加载rt.jar里的类,刚刚那个System就是属于rt.jar包里面的,ExtClassLoader负责加载JRE/lib/ext/*.jar这个目录文件夹下的文件。而AppClassLoader负责加载ClassPath目录下的所有jar文件及目录。
最后一级是我们自定义的加载器,他们的父类都是AppClassLoader。
下面我们来看看ExtClassLoader所加载的类。
 
 
点击下一步:
 
 点击浏览,我们把jar文件放到jre/lib/ext文件夹下,
 
 这个文件夹里的类是有谁加载的呢?是由ExtClassLoader加载器加载的。

这里一定要注意一个问题:就是你的程序的运行时环境。上面我在E:\java\jdk 6\jre6\lib\ext,而我程序运行的jre在E:\Genuitec\Common\binary\com.sun.java.jdk.win32.x86_1.6.0.013\lib目录下。所以,虽然我生成了itcast.jar文件,却没有任何效果。所以把itcast.jar文件放在E:\Genuitec\Common\binary\com.sun.java.jdk.win32.x86_1.6.0.013\lib\ext目录下,我们再来看看效果。
 
 

我们看到,同样是ClassLoaderTest这个类,现在却是被ExtClassLoader加载的了,通过这个程序我们明白了ExtClassLoader类加载器是专门加载jre\lib\ext/下的jar包的。

下载大家应该有一个疑问。刚才不是有AppClassLoader加载器加载的么?现在怎么不由AppClassLoader加载器加载了呢?而是有ExtClassLoader类加载器加载了啊?这就是优先级的问题了。爸爸找不到的时候,由儿子加载,爸爸找到了,就由爸爸加载了,这是什么原理?这就是类加载器的委托机制。

再说类加载器的委托机制之前,我们再来说说另一个问题。

再来说说继承树,除了系统自带了类加载器,我们还可以自定义类加载器。然后把自己的类加载器挂在树上。作为某个类加载器的孩子。所有自定义类加载器都要继承ClassLoader。实现里面的一个方法ClassLoader()如下:
 
 

javaJVM用到一个类,它派谁去加载这个类呢?

他首先派当前线程的类加载器去加载第一个类。java运行起来可能有多个线程,每个线程都有一个获取类上下文加载器的方法getContextClassLoader(ClassLoader cl),通过setContextClassLoader(ClassLoader cl)方法,设置该线程的上下文 ClassLoader。一旦这个线程把第一个类加载进来以后,而第一个类引用或继承了第二个类,那么第二个类有谁来加载呢?也是由刚才加载第一个类的类加载器来加载第二个类。

如果我不想使用线程类加载器,我想强制使用我的类加载器。直接用你的类加载器对象classLoader.loadClass()方法也可以。

有可能出现这个情况:派AppClassLoader类加载器去加载System类,结果没有找到。那他会抛异常么?不会。他会交给上级。但这个过程是反得。当我们派AppClassLoader去加载System的时候,他先不找,让谁找呢?交给爸爸ExtClassLoader去找。爸爸收到命令以后找不找呢?以此类推,不找,交给爷爷BootStrap。爷爷没有爸爸了, 那就自己找吧,找到就ok了;如果爷爷没找到,那就回来,推给儿子ExtClassLoader找;儿子如果没有找到,推给孙子AppClassLoader去找;孙子AppClassLoader如果还是找不到,那就会抛出异常了。孙子就是这个请求的发起者。不会说再交给孙子的儿子去找了。他有好多儿子呢,交给谁呀?

这样的好处在哪里呢?可以集中管理,不会出现多份字节码重复的现象。有两个类要再在System,如果让底层的类加载器加载,可能会出现两份字节码。而都让爷爷加载,爷爷加载到已有,当再有请求过来的时候,爷爷说:哎,我加载过啊,直接把那份拿出来给你用啊。就不会出现多份字节码重复的现象。

现在有一道面试题:能不能自己写一套java.lang.System.?
分析:你写了也白写,因为类加载器加载,直接到爷爷那里去找,找成功了,分本就不回来理你的那个。
答案:通常不可以,因为委托机制委托给爷爷,爷爷在rt.jar包加载到这个类以后就不会加载你自己写了那个System类了。但是,我也有办法加载,我写一个自己的类加载器,不让他用委托机制,不委托给上级了,就可以了。
 

今天学习的PPT
 
 




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多