ClassLoader的等级加载机制。

    ClassLoader顾名思义就是类加载器,负责将Class加载到JVM中,他就好比开会时门口的接待员,负责给进入会场的嘉宾发放入会证明,入会的嘉宾分为VIP会员、黄金会员、白金会员和普通会员等。对应的接待室也会分为VIP会员接待室、黄金会员接待室、白金会员接待室和普通接待室,不同等级的会员会被分到不同的接待室接待。所有的会员要想进入会场得有入会证明才行,一旦会员进入会场就会根据接待室的等级标识他们,也就是会员的身份由接待室决定。如果你是一位大佬但是你不是VIP接待室接待的,那么对不起,你仍然不是VIP会员。当然对你是不是VIP会员会有严格的审查规定,如果你是也不会冤枉你,但是如果你想混进来就另当别论了。
    在上面的会员进入会场的规则中,如何保证不同等级的会员通过不同的会员接待室进入会场?因为可能有些会员自己并不能正确的找到接待自己的接待室,也有可能有些会员会冒充更高级的会员身份混进去,所以必须要有机制能够保证所有会员都被正确的接待室接待进入会场,而且一个会员只能被一个接待室接待,不能出现被两个接待室重复接待的情况,也就是不能同时拿到两个入场证明,从而保证接待的一致性。如何设计这个接待规则呢?
    ClassLoader就设计了这样一种接待机制,这个机制就是上级委托接待机制。他是这样的:任何一个会员到达任何一个会员接待室时,这个接待室首先会检查这个会员是否已经被自己接待过,如果已经接待过,则拒绝本次接待,也就是不再发入会证明了,如果没有接待过,那么会向上询问这个会员是否应该在上一级的更高级别的接待室接待,上级接待室会根据他们的接待规则,检查这个会员是否已经被接待过,如果已经接待过,同样的处理方法,将已经接待的结果反馈给下一级,如果也没有接待过,再向更高一级(如果有更高一级的话)接待室转发接待请求,更高一级也是同样的处理方法,直到有一级接待室接待或者告诉他下一级这个会员不是自己接待的这个结果:如果这个会员来到的这个接待室得到他上一级的接待室反馈认为这个会员没有被接待,并且也不应该由他们接待,这个接待室将会正式接待这个会员,并发给他入会证明,这个会员就被定义为这个接待室等级的会员。
    这种接待规则看上去看点麻烦,但是他却能够保证所有的会员都被正确的接待室接待,会员的身份也不会错,也不存在冒充身份的会员。
    整个JVM平台提供三层ClassLoader,这三层ClassLoader可以分为两种类型,可以理解为为接待室服务的接待室和为会员服务的接待室两种。

  1.         Bootstrap ClassLoader,这个ClassLoader就是接待室服务自身的,他主要加载JVM自身工作需要的类,这个ClassLoader完全是由JVM自己控制的,需要加载哪个类、怎么加载都由JVM自己控制,别人也访问不到这个类,所以这个ClassLoader是不遵守前面介绍的加载规则的,他仅仅是一个类的加载工具而已,既没有更高一级的父加载器,也没有子加载器。
  2.         ExtClassLoader,这个类加载器有点特殊,他是JVM自身的一部分,但是他的血统也不是很纯正,他并不是JVM亲自实现的,我们可以理解为这个类加载器是那些与这个大会合作单位的员工会员,这些会员既不是JVM内部的,也和普通的外部会员不同,所以就由这个类加载器。他服务的特定目标在System.getProperty("java.ext.dirs")目录下。
  3.         AppClassLoader,这个类加载器就是专门为接待会员服务的,他的父类是ExtClassLoader。他服务的目标是广大普通会员,所有在System.getProperty("java.class.path")目录下的类都可以被这个类加载器加载,这个目录就是我们经常用到的classpath。

    如果我们要实现自己的类加载器,不管你是直接实现抽象类ClassLoader,还是继承URLClassLoader类,或者其他子类,他的父加载器都是AppClassLoader,因为不管调用哪个父类构造器,创建的对象都必须最终调用getSystemClassLoader()作为父加载器。而getSystemClassLoader()方法获取到的正是AppClassLoader。
    很多文章在介绍ClassLoader的等级结构时把Bootstrap ClassLoader也列在ExtClassLoader的上一级中,其实Bootstrap ClassLoader并不属于JVM的类等级层次,因为Bootstrap ClassLoader并没有遵守ClassLoader的加载规则。另外Bootstrap ClassLoader并没有子类,ExtClassLoader的父类也不是Bootsrap ClassLoader,ExtClassLoader并没有父类,我们在应用中能提取到的顶层父类是ExtClassLoader。
    ExtClassLoader和AppClassLoader都位于sun.misc.Launcher类中,他们是Launcher类的内部类。如下图所示。
    
    ExtClassLoader和AppClassLoader都继承了URLClassLoader类,而URLClassLoader又实现了抽象类ClassLoader,在创建Launcher对象时首先会创建ExtClassLoader,然后将ExtClassLoader对象作为父加载器创建AppClassLoader对象,而通过Launcher.getClassLoader()方法获取的ClassLoader就是AppClassLoader对象。所以如果在Java应用中没有定义其他ClassLoader,那么除了System.getProperty("java.ext.dirs")目录下的类是由ExtClassLoader加载外,其他类都由AppClassLoader来加载。
    JVM加载class文件到内存有两种方式。

  •         隐式加载:所谓隐式加载就是不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存的方式。例如,当我们在类中继承或者引用某个类时,JVM在解析当前这个类时发现引用的类不在内存中,那么就会自动将这些类加载到内存中。
  •        显式加载:相反的显式加载就是我们在代码中通过调用ClassLoader类来加载一个类的方式,例如,调用this.getClass.getClassLoader().loadClass()或者Class.forName(),或者我们自己实现的ClassLoader的findClass()方法等。

    其实这两种方式是混合使用的,例如,我们通过自定义的ClassLoader显式加载一个类时,这个类中又引用了其他类,那么这些类就是隐式加载的。

    

猜你喜欢

转载自blog.csdn.net/en_joker/article/details/81389495