• JVM类加载机制


    JVM类加载机制

    类加载过程

    详细链接: https://www.processon.com/view/link/61e31970e0b34d1be7f8ac96

    类加载过程.png

    • ClassLoader.loadClass()的类加载过程
      • 加载: 从磁盘上将.class的字节码文件加载到JVM内存中去,此时会做一些简单的校验,校验通过后,会生成一个java.lang.Class对象,作为这个类各种数据再方法区的入口
      • 验证: 校验字节码文件的正确性,看看是否符合JVM字节码规范,比如魔数,主次版本号等
      • 准备: 将类中的静态变量分配内存并且赋默认值
      • 解析: 将符号引用(比如方法名)转变为直接引用(JVM内存地址),这就是所谓的静态链接的过程
        • 静态链接: 类加载期间,将符号引用转变为直接引用
        • 动态链接: 程序运行期间,将符号引用转变为直接引用,比如方法中调用方法,JVM解析的时候会对被调用的方法名指向一个内存地址,通过这个地址找到对应的方法,最常见的就是多态,有不同的实现,只有到运行的时候才知道
      • 初始化: 对类的静态变量初始化为指定值,执行静态代码块
    • 类被加载到方法区中包含
      • 运行时常量池
      • 类型信息
      • 字段信息
      • 方法信息
      • 类加载器的引用
      • 对应class实例的引用
    • 注意: 类加载机制是懒加载,部署war包使用时才会加载,静态代码块比构造函数先执行,类没加载完,不会调用构造函数

    类加载器和双亲委派机制

    类加载器

    • 引导类加载器(BootStrap): 加载jre/lib目录下的核心类库

    • 拓展类加载器(Ext): 加载支撑JVM运行的jre/lib/ext目录下的jar

    • 应用程序类加载器(App): 加载classpath路径下的类包,主要是加载用户自己写的类

    • 自定义类加载器: 加载用户自定义路径下的类。特殊需要的时候才自定义类加载器,比如Tomcat会自定义类加载器,默认是应用程序加载器

    • sum.misc.Launcher可以理解为所有类的父类加载器,C++先初始化Launcher类后,在通过此类生成拓展类、应用程序类加载器等

    • 问题: 为什么引导类加载器不是Launcher类生成的

      • 因为引导类加载器是由C++直接生成的对象

    类加载器初始化过程

    • 创建JVM启动器实例: sun.misc.Launcher,此类为单例模式,保证只有一个实例
    • Launcher类创建两个类,ExtClassLoader和AppClassLoader
    • JVM默认使用Launcher的ClassLoader方法返回的加载器AppClassLoader的实例加载我们的应用程序

    双亲委派机制

    双亲委派机制.png

    • 当应用程序类加载器加载某类时,如果没有找到,就会向上让扩展类加载器去加载
    • 如果扩展类加载器也没有该类,就会继续向上让引导类加载器去加载
    • 如果引导类加载器也没有该类,那么就向下继续委托
    • 如果都没有找到,那么就让应用程序类加载器去加载
    为什么要先用应用程序类加载器去加载代码,而不是直接用引导类加载器
    • 因为大部分的类都是用户自己定义编写的,也就是大部分的类文件都存放在应用程序类加载器的指定类路径下,如果不从这里开始会导致效率降低
    什么是双亲委派机制
    • 双亲委派机制就是加载某个类时先委托父加载器去寻找目标类,找不到再委托上层父加载器,如果所有父加载器都没有自己类路径下找到目标类,那么就会交给最开始的类加载器去加载该目标类
    为什么要设计双亲委派机制
    • 双亲委派机制可以避免类的重复加载
    • 沙箱安全机制,保证核心类不被篡改

    全盘委托机制

    • 全盘委托是指当一个ClassLoder装载一个类时,除非显示的使用另外一个ClassLoder,该类所依赖及引用的类也由这个ClassLoder载入

    如何自定义类加载器

    • 代码思路
      • 继承java.lang.ClassLoader类
      • 重写ClassLoader.findClass()方法,此方法通过IO读取自定类文件路径
      • 重写ClassLoader.loadClass()方法,加载类

    打破双亲委派机制

    • 加载一个类,不使用父加载器,直接由自定义类加载器加载类
    • 代码思路
      • 重写ClassLoader.loadClass()方法后
      • 在里面调用父加载器的方法处添加判断逻辑,如果是自定义类路径,就调用自定义的findClass方法
    Tomcat为什么要打破双亲委派机制
    • 对于一个web容器来说,可能需要部署多个应用程序,不同的应用程序可能依赖相同类库的不同版本,但是双亲委派机制已经实现了避免重复类加载,因此要保证每个版本的类库是独立的,要保证相互隔离
    • 部署在同一个web容器中相同类库的版本可以共享,否则多个应用程序此时都是同一类库的版本,会导致进行加载多次,浪费资源
    • 处于安全方面考虑,Tomcat也有自己的类库,也需要进行隔离处理,否则会导致与应用程序的类库混淆
    • 每个jsp对应一个类加载器,这样是为了方便热部署
    Tomcat这种类加载机制是否违背了双亲委派机制
    • 很显然是的,Tomcat为了实现隔离性,每个webappClassLoader加载自己目录下的类文件,不会传递给父加载器,打破了双亲委派机制
    • 首先自己尝试去加载某类,如果找不到再代理给父加载器,其目的是优先加载web应用自己定义的类,它父类加载器就是AppClassLoader
    • web应用是通过Class.forName()调用交给系统类加载器的,因为Class.forName的默认类加载器是系统类加载器
    Tomcat怎么实现热加载
    • 单独用一个线程时刻去监听jsp文件路径下文件夹的变动,比如说1s监听一次,如果有变动,把之前的类加载器置空,再重新赋值最新的类加载器,之前的等待gc回收
    Tomcat类加载图

    Tomcat自定义类加载器-打破双亲委派机制.png

  • 相关阅读:
    抖音API接口汇总及解析式(网络爬虫)
    齐岳|马钱苷酸小麦麦清白蛋白纳米粒|雷公藤红素乳清白蛋白纳米粒Celastrol-whey protein
    vite vue引入svg图标及封装 (轻松上手)
    基于SSM的文化培训学校网站的设计与实现
    混沌映射与动态学习的自适应樽海鞘群算法-附代码
    “蔚来杯“2022牛客暑期多校训练营(加赛) B题: Bustling City
    [Linux系统编程]_进程(二)
    Auto.js中的悬浮窗
    图形库篇 | EasyX | 图像处理
    jenkins 中文乱码及执行报错
  • 原文地址:https://blog.csdn.net/qq_41804775/article/details/136359555