zoukankan      html  css  js  c++  java
  • 三层类加载器、双亲委派模型--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及以后虽然维持着三层类加载器和双亲委派的架构,但类加载的委派关系发生了变动

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

  • 相关阅读:
    c# linq 实现 m选n 组合
    asp.net web forms 部署到 centos mono 总结
    asp.net webform entityframework 部署到 centos mono
    css 固定表头
    asp.net webforms 用户自定义控件验证
    C#创建编译时动态属性对象和运行时动态属性对象(linq 动态列名)
    monodevelop 在 linux 安装后 debug 报错 Debugger operation failed
    c# winform 在新的应用程序域运行 wpf
    c# 图像压缩
    java 图像压缩
  • 原文地址:https://www.cnblogs.com/dwwzone/p/12851876.html
Copyright © 2011-2022 走看看