三层类加载器双亲委派模型--Java类加载器总结分析

Posted dwwzone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三层类加载器双亲委派模型--Java类加载器总结分析相关的知识,希望对你有一定的参考价值。

Java类加载过程总结分析这篇博文中,我们提到,JVM类加载的第一步就是”加载“,而这一步就是由Java的类加载器完成

类加载器的作用:通过一个类的全限定名来获取描述该类的二进制字节流

注意:对于任意一个类,都必须由它的类加载器和这个类本身一起确立其在JVM中的唯一性,即 :即使两个类来源同一Class文件,被同一个JVM加载,但只要加载他们的类加载器不同,这两个类就不相等!

package Q2;

import java.io.IOException;
import java.io.InputStream;

public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {
        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.indexOf(".") + 1)+".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name,b,0,b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };

        Object obj = myLoader.loadClass("Q2.ClassLoaderTest").newInstance();

        System.out.println(obj.getClass());
        System.out.println(obj instanceof Q2.ClassLoaderTest);
    }
}

输出结果为

class Q2.ClassLoaderTest
false

以上程序构造了一个简易的类加载器,用这个类加载器去加载测试类,根据结果显示,obj对象的确是Q2.ClassLoaderTest的实例,但是类型检查却返回false,

这是因为虚拟机中同时存在了两个ClassLoaderTest类,一个由虚拟机的应用程序类加载器加载,一个由自定义类加载器加载,虽然来自同一Class文件,但仍属于两相互独立的类,在对象所属对象类型检查时即返回false。

三层类加载器

  Java中存在以下三类类加载器

    1. 启动类加载器(Bootstrap ClassLoader):这个类加载器负责加载存放在<JAVA_HOME>lib目录,或者被-Xbootclasspath参数指定的路径中存放的,且是JVM能够识别的类库。只有这个类加载器是使用C++语言实现,是JVM的一部分,其他类加载器全部由java语言实现,独立存在于JVM外部,且都继承于java.lang.Classloader抽象类。

    2. 扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>homeext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库,开发者可以直接使用此类加载器加载Class文件。

    3. 应用程序类加载器(Application ClassLoader):负责加载用户类路径(ClassPath)上的所有类库,开发者同样可以直接使用此类加载器,如果没有指定,则默认使用此类加载器。

  三层类加载器、双亲委派模型

                                       技术图片

  JDK9之前,Java应用都由这三种类加载器相互配合完成,如果由必要,用户可以自定义类加载器进行拓展。

  如图所示, 除了最顶层的启动类加载器外,其余的加载器都必须有父类加载器,只不过这里一般使用组合关系来复用父类的代码。

  双亲委派模型工作过程

    如果一个类加载器收到了类加载请求,它自身不会第一时间去尝试加载这个类,而是把这个请求委派给自己的父类加载器去执行,因此所有的加载请求最终都会传输到最顶层的启动类加载器,只有父类加载器反馈自己无法完成加载时(搜索范围内没有找到需要的类),子类加载器才会去尝试自己完成加载。

  双亲委派模型的好处

    可以保证无论哪一个类加载器要要加载类C,都可以保证类的唯一性。如无论那个类加载器要加载Object类,最终都会轮到启动类加载器去加载Object类。反之,如果没有双亲委派的话,由各个类自行加载,那么就可能出现多个不同的类C,导致程序混乱。

  双亲委派模型的源码分析:

protected synchronized Class<?> loadClass(String name,boolean resolve)
            throws ClassNotFoundException {
        //首先检查请求的类是否已经加载过
        Class c = findLoadedClass(name);
        if (c == null) {
            try {
                if (parent != null) {
                     c = parent.loadClass(name,false);  //父类加载器不为空,调用父类加载器
                }else {
                    c = findBootstrapClassOrNull(name); //父类加载器为空,则使用启动类加载器
                }
            }catch (ClassNotFoundException e) {
                //如果父类加载器抛出ClassNotFoundException
                //说明父类无法完成加载请求
            }
            if (c == null) {
                //在父类无法加载时,调用本身类加载器进行加载
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }

  注意:

     在JDK9以后,Java类库实行模块化,不同的类库组成不同的模块,类加载系统也随之发生了一些变化,比如:扩展类加载器被平台类加载器替代等

                   技术图片

 

  如上图所示,JDK9及以后虽然维持着三层类加载器和双亲委派的架构,但类加载的委派关系发生了变动

  当平台及应用程序类加载器收到类加载请求,在委派给父类加载器之前,会判断该类能否归属到某一个系统模块中,如果可以找到归属关系,就优先委派给负责加载那个模块的加载器进行加载!

 

以上是关于三层类加载器双亲委派模型--Java类加载器总结分析的主要内容,如果未能解决你的问题,请参考以下文章

三层类加载器双亲委派模型--Java类加载器总结分析

Java双亲委派机制在Android的应用

Java双亲委派机制在Android的应用

Java双亲委派机制在Android的应用

java 类加载器双亲委派模型

Java双亲委派模型