• 2022.18 Java 类加载回顾总结


    Java类的生命周期如下图:
    这里写图片描述
    主要包括加载、连接、初始化、使用、卸载几个阶段,其中类加载是最先要做,它是JVM运行时如何把用户编译后的class文件转成二进制字节流的过程,要解决从哪里加载class以及加载哪些class的问题,是通过ClassLoader类加载器实现的。
    ClassLoader是Java的核心组件,所有的Class都是由ClassLoader进行加载的,ClassLoader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例,然后交给Java虚拟机进行链接、初始化等操作。
    JDK8及以前提供了三个类加载器:
    1、启动类加载器,Bootstrp loader,由C/C++实现,属最高层,JVM启动时创建,通常由与操作系统相关的本地代码实现,是最根基的类加载器,它主要负责加载%JAVA_HOME%/jre/lib,-Xbootclasspath参数指定的路径下的类库。注意它只能加载特定名称的类库(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类,这些类也只能被它加载),如rt.jar,一些java开头的jar包等,其它应用jar宝即使放在- Xbootclasspath参数指定的路径中下,它也不会加载。
    2、扩展类加载器,由sun.misc.Launcher$ExtClassLoader实现,只有一个实例。Bootstrp loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader。ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext,此路径下的所有classes目录以及java.ext.dirs系统变量指定的路径中类库。
    3、应用类加载器,由sun.misc.Launcher$AppClassLoader实现,只有一个实例。Bootstrp loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为 ExtClassLoader。AppClassLoader主要负责加载classpath所指定的位置的类或者是jar包。它也是Java程序默认的类加载器,如果应用程序中没有定义自己的加载器,则该加载器也就是默认的类加载器。ClassLoader中有个getSystemClassLoader方法,此方法返回的正是AppclassLoader。
    JDK9引入了模块化以后,扩展类加载器变为了平台类加载器,Platform ClassLoader,各加载器默认加载的模块也有差异。
    不同类加载器看似是继承关系,实际上是包含关系。在下层加载器中,包含着上层加载器的引用。每一个类加载器,都拥有一个独立的类名称空间,比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即使这两个类源自同一个Class文件,被同一个虚拟机加载,只要加载他们的类加载器不同,那这两个类就必定不相等。
    每个类加载器都有自己的命名空间,命名空间由该类加载器及所有的附加在其所加载的类组成;Java类加载的使用双亲委派模型,有两个特点:
    (1)可见性:子类加载器可以访问父加载器加载的类型,但是反过来是不允许的,不然,因为缺少必要的隔离,我们就没有办法利用类加载器去实现容器的逻辑。
    (2)单一性:由于父加载器的类型对于子加载器是可见的,所以父加载器中加载过的类型,就不会子加载器中重复加载。但是注意,类加载器”邻居“间,同一类型仍然可以被加载多次,因为互相并不可见。
    但双亲委派模型不能满足所有的场景,有时候,启动类加载器所加载的类型,是可能要加载用户代码的,比如JDK内部的ServiceProvider/ServiceLoader机制,用户可以在标准API框架上,提供自己的实现。JDK也需要提供些默认的参考实现,例如:Java中的JNDI、JDBC、文件系统、Cipher等很多方面,都是利用的这种机制,因此引入了线程上下文加载器。
    JDK还为核心类库提供了一层保护机制,不管是自定义的类加载器,还是系统类加载器亦或是扩展类加载器,最终都必须调用java.lang.defineClass(String, byte[], int, int, ProtectionDomain)方法,而该方法会执行preDefineClass()接口,该接口中提供了对JDK核心类库的保护。
    一般类加载器不会加载jar包中的jar包,SpringBoot打包时能将所有依赖也打包到一个jar包里运行,是因为它自定义了类加载器,会加载里面的jar包。
    自定义类加载器能够实现应用隔离、类的动态加载或对字节码文件进行加解密操作。例如Tomcat、Spring等中间件和组件框架都在内部实现了自定义的加载器,并通过自定义加载器隔离不同的组件模块,这种机制比C/C++程序要完善许多。
    Java虚拟机规范并没有明确要求类加载器的加载机制一定要使用双亲委派机制,只是建议采用这种方式而已。比如在Tomcat中,类加载器所采用的的加载机制和传统的双亲委派模型有一定区别,当缺省的类加载器接收到一个类的加载任务时,首先会由它自行加载,当它加载失败时,才会将类的加载任务委派给它的超类加载器去执行,这同时也是Servlet规范推荐的一种做法。
  • 相关阅读:
    张量自动求导和计算图
    34 深入浅出 x86 中断机制(下)
    33 深入浅出 x86 中断机制(上)
    使用STM32 PWM控制LCD背光
    32 获取物理内存容量 下
    ucGUI内存管理相关
    STM32 SPI双机通信参考
    IAR_UCOSII_UCOSIII
    每日总结
    每日总结
  • 原文地址:https://www.cnblogs.com/doit8791/p/16209906.html
Copyright © 2020-2023  润新知