• Java之类加载器(Class Loader)


    JVM默认有三个类加载器:

    • Bootstrap Loader

      Bootstrap Loader通常有C编写,贴近底层操作系统。是JVM启动后,第一个创建的类加载器。

    • Extended Loader

      Extended Loader由Java编写,由Bootstrap Loader创建。JVM启动后,第二个被创建的类加载器。在Oracle JDK中,对应sum.misc.Launcher$ExtClassLoader($表示内部类)。

    • System Loader

      System Loader由Java编写,同样由Bootstrap Loader创建。JVM启动后,第三给被创建的类加载器。在Oracle JDK中,对应sum.misc.Launcher$AppClassLoader

         

    JVM启动后,类加载器的创建顺序如下:

    1. JVM创建Bootstrap Loader;
    2. 由Bootstrap Loader创建Extended Loader;
    3. 设置Bootstrap Loader为Extended Loader的父类
    4. 用Bootstrap Loader创建System Loader;
    5. 设置Extended Loader为System Loader的父类

    如下图:

    从创建过程可见,类加载器间是有层级关系的

    当类加载器有加载任务时,会先把加载任务交给父加载器,如果父加载器无法加载,才由自己加载。所以加载类的时候,会以Bootstrap Loader → Extended Loader → System Loader的加载类。如果所有加载器加载类失败,抛出java.lang.NoClassDefFoundError异常。

    每个类加载器,会到其指定的目录下,根据类名加载类文件。三个默认类加载器的指定目录保持在JVM的系统属性里。

    Bootstrap Loader

    sun.boot.class.path

    可以在编译时期,使用-bootclasspath指定。

    Extended Loader

    java.ext.dirs

    System Loader

    java.class.path

    可以在运行程序时,使用-cp指令覆盖CLASSPATH系统环境变量。

    可以使用System.getProperty()方法获取实际值。

       

    三个默认类加载器在程序启动后,就无法更改它们的搜索目录。如果在程序运行过程中,打算动态加载其他路径下的类,可以创建java.net.URLClassLoader实例,使用新的类加载器。

    URLClassLoader类创建实例时,需要java.net.URL数组作为参数指定新的类加载搜索路径。

    ClassLoader loader = new URLClassLoader(new URL[] {new URL(pathA), new URL(pathB)});

    loader.loadClass(clzName);

    URLClassLoader类的实例,将由Bootstrap Loader创建,指定父加载器为System Loader

    由于使用URL协议,可以指定远程服务器上的类文件,使用本地路径时,注意添加前缀"file:/"

       

    类加载器可以使用loadClass()方法加载类,默认不会执行类的静态初始区块。但会在第一次新建该类实例的时候执行静态初始区块

    可以使用getParent()获取类加载器的父加载器。自定义对象默认用System Loader加载,可以使用Class.getClassLoader()获取加载该类的类加载器。

    // 获取System Loader

    ClassLoader sysClassLoader = Empty.class.getClassLoader();

    // 获取Extended Loader

    ClassLoader extClassLoader = sysClassLoader.getParent();

    // 获取Bootstrap Loader

    ClassLoader bootClassLoader = extClassLoader.getParent();

       

    System.out.println(sysClassLoader);

    System.out.println(extClassLoader);

    System.out.println(bootClassLoader);

    输入如下:

    sun.misc.Launcher$AppClassLoader@73d16e93

    sun.misc.Launcher$ExtClassLoader@15db9742

    null

    获取Extended Loader的父加载器时,返回值为null,但并不代表它没父加载器。因为Bootstrap Loader通常由C实现,在Java中没实际类实例来表示,所有会显示null

    标准API的类(包括数组对象,包装器),都是由Bootstrap Loader加载的。

    // 以下均输出null

    System.out.println(String.class.getClassLoader());

    System.out.println(int[].class.getClassLoader());

    System.out.println(Integer.class.getClassLoader());

    System.out.println(Class.class.getClassLoader());

       

    同一个类文件,由同一个类加载器(实际加载的那个类加载器,注意加载任务会先向父加载器传递)多次加载,只有一个Class实例;如果由不同的类加载器加载,会由不同的Class实例。

    参考资料:《Java学习笔记》 第17章

  • 相关阅读:
    Android之快速搭建应用框架
    oracle hints merge 视图合并
    十年数据架构经验,告诉你业务化大数据中台最核心的四点
    Cinder LVM Oversubscription in thin provisioning
    Oracle 20c数据库开启原生的区块链表、AutoML以及持久化内存支持
    学习三十五
    学习三十五
    认知类和对象的关系
    认知类和对象的关系
    认知类和对象的关系
  • 原文地址:https://www.cnblogs.com/foundkey/p/9790516.html
Copyright © 2020-2023  润新知