Java类加载机制(类加载器,双亲委派模型,热部署示例)

news2024/11/26 23:22:23

Java类加载机制

  • 类加载器
    • 类加载器的执行流程
    • 类加载器的种类
    • 加载器之间的关系
    • ClassLoader 的主要方法
    • Class.forName()与ClassLoader.loadClass()区别
  • 双亲委派模型
    • 双亲委派 类加载流程
    • 优缺点
  • 热部署简单示例

类加载器

类加载器的执行流程

在这里插入图片描述

类加载器的种类

AppClassLoader
应用类加载器,默认的系统类加载器,负责加载java应用种classpath中的类

设置 classpath java -cp D:\aaa Main.class 获取 classpath
System.getProperty(“java.class.path”)

ExtClasLoader
扩展类加载器,负责加载扩展目录中的java类。

设置扩展目录:java -Djava.ext.dirs=“D:\aa” Main.class
获取扩展目录:System.getProperty(“java.ext.dirs”)

从java9开始,扩展机制被移除,加载器被PlatFormClassLoader取代

BootStrapClassLoader
启动类加载器,负责加载JDK核心类库(/jre/lib)
由 C++/C 语言编写,无法通过java代码打印

用户自定义的ClassLoader
继承抽象类 ClassLoader 的自定义类加载器

加载器之间的关系

在这里插入图片描述
从JVM的角度来看,一共有两种类加载器,BootStrapClassLoaderjava 自定义的类加载器
其中 BootStrapClassLoader 是使用C/C++语言编写的 最高级别的 类加载器

Java 自定义的类加载器 都继承于抽象类 ClassLoader ,并且内部有一个parent 属性,指向父加载器(是组合,不是继承
ExtClassLoader 的 parent属性 是null,实际是通过native方法 关联 BootStrapClassLoader ,所以ExtClassLoader 的父类加载器 是 BootStrapClassLoader

JDK自带的类加载器有:BootStrapClassLoader,ExtClassLoader,AppClassLoader
三者并不是集成关系,而是组合

用户可以继承ClassLoader 来实现自己的类加载器,默认的父类加载器是 AppClassLoader

ClassLoader 的主要方法

//返回该类加载器的超类加载器
public final ClassLoader getParent()
 
//加载名称为name的类,返回结果为java.lang.Class类的实例。如果找不到类,
//则返回 ClassNotFoundException异常。该方法中的逻辑就是双亲委派模式的实现
public Class<?> loadClass(String name) throws ClassNotFoundException
 
//查找二进制名称为name的类,返回结果为java.lang.Class类的实例。
//这是一个受保护的方法,JVM鼓励我们重写此方法,需要自定义加载器遵循双亲委托机制,
//该方法会在检查完父类加载器之后被loadClass()方法调用。
protected Class<?> findClass(String name) throws ClassNotFoundException
 
//根据给定的字节数组b转换为Class的实例,off和len参数表示实际Class信息在byte数组中的位置和长度,
//其中byte数组b是ClassLoader从外部获取的。
//这是受保护的方法,只有在自定义ClassLoader子类中可以使用。
protected final Class<?> defineClass(String name, byte[] b,int off,int len)
 
//链接指定的一个Java类。使用该方法可以使用类的Class对象创建完成的同时也被解析。
//链接阶段主要是对字节码进行验证,
//为类变量分配内存并设置初始值同时将字节码文件中的符号引用转换为直接引用。
protected final void resolveClass(Class<?> c)
 
//查找名称为name的已经被加载过的类,返回结果为java.lang.Class类的实例。这个方法是final方法,无法被修改。
protected final Class<?> findLoadedClass(String name)
 
//它也是一个ClassLoader的实例,这个字段所表示的ClassLoader也称为这个ClassLoader的双亲。
//在类加载的过程中,ClassLoader可能会将某些请求交予自己的双亲处理。
private final ClassLoader parent;

Class.forName()与ClassLoader.loadClass()区别

public static Class<?> forName(String className)

  1. 是类方法
  2. 默认使用系统类加载器进行加载
  3. 加载一个指定的类,会对类进行初始化,执行类中的静态代码块,以及对静态变量的赋值等操作, 一般用于加载驱动,例如jdbc驱动

public Class<?> loadClass(String name)

  1. 是成员方法
  2. 懒加载,只是加载,不会解析更不会初始化所反射的类

双亲委派模型

rents Delegation Model
注意:双亲并不是指存在父母两个类加载器,实际只有一个parent 父加载器 并且是作为加载器的属性,而不是继承,可以理解为 雌雄同株

在这里插入图片描述

双亲委派 类加载流程

  1. 当一个类开始进行加载时,会先从判断这个类是否已经被加载了,如果已经加载了,返回已加载的类Class对象
  2. 如果还没有被加载,通过parent属性 将加载请求传递给上层类加载器进行加载
  3. 一直调用到扩展类加载器(ExtClassLoader),如果都没有找到已经被加载的Class对象,此时parent == null ,通过 findBootstrapClassOrNull() 方法传递给 BootStrapClassLoader 进行类加载
  4. 如果BootStrapClassLoader 没有加载成功,其下层类加载器开始尝试进行加载
  5. 如果一直到最底层的类加载器(用户自定义的类加载器)都没有加载成功,则抛出ClassNotFoundException异常

一句话概括:子类加载器调用父类加载器去加载类

源码如下:

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    // 如果有parent ,交给parent 类加载器进行加载
                    c = parent.loadClass(name, false);
                } else {
                    //没有parent,通过native方法委派给BootStrapClassLoader 进行加载
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ClassNotFoundException thrown if class not found
                // from the non-null parent class loader
            }

            if (c == null) {
                //如果父类没有加载出Class对象,开始尝试自己加载
                long t1 = System.nanoTime();
                //根据类的全限定名,获取Class 对象,分为两步
                //1.根据类的全限定名,获取字节码对象
                //2. 根据字节码的二进制流,调用defineClass()方法,生成Class对象
                c = findClass(name);

                // this is the defining class loader; record the stats
                sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                sun.misc.PerfCounter.getFindClasses().increment();
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

优缺点

优点:

  1. 防止重复加载,确保一个类的全局唯一性

当一个类加载器要加载一个类,首先交给父类加载器进行加载,并且加载过程由syncronized锁控制。避免重复加载同一个类

  1. 确保JVM的安全,防止用户用自己编写的类替换核心类库中的类

例如:用户自定义一个java.lang.String的类,通过双亲委派模型,加载的还是核心类库中的String类,不会被用户自己定义的String类替换

缺点:
父类加载器不能访问子类加载器加载的类,有时需要打破双亲委派模型,例如Tomcat

热部署简单示例

public class MyClassLoader extends ClassLoader{
    /**
     * 文件路径
     */
    final private String path;

    protected MyClassLoader(String path) {
        this.path = path;
    }

    /**
     * 重写findClass,实现自己的类加载器
     * 1. 获取class文件的字节流
     * 2. 调用defineClass 将字节流 转化为 CLass 对象
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 读取字节码的二进制流
            byte[] b = loadClassFromFile(name);
            // 调用 defineClass() 方法创建 Class 对象
            Class<?> c = defineClass(name, b, 0, b.length);
            return c;
        } catch (IOException e) {
           throw new ClassNotFoundException(name);
        }
    }

    /**
     * 如果要遵循双亲委派模型,则不用重写loadClass方法
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        //为了测试方便,这里简单打破下双亲委派模型,先从自定义类加载器进行加载
        //如果不想打破双亲委派模型,path路径可以设置非classpath下的路径,手动复制class文件到对应目录下
        synchronized (getClassLoadingLock(name)) {
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                //先从自定义类加载器进行加载,这里打破了双亲委派模型
                try {
                    c = findClass(name);
                } catch (ClassNotFoundException ignored) {
                }
                //自定义类加载器加载失败,交给父 类加载器
                if(c == null){
                    return super.loadClass(name);
                }
            }
            return c;
        }
    }

    private byte[] loadClassFromFile(String name) throws IOException {
        String fileName = name.replace('.', File.separatorChar) + ".class";
        String filePath = this.path + File.separatorChar + fileName;

        try (InputStream inputStream = new FileInputStream(filePath);
             ByteArrayOutputStream byteStream = new ByteArrayOutputStream()
        ) {
            int nextValue;
            while ((nextValue = inputStream.read()) != -1) {
                byteStream.write(nextValue);
            }
            return byteStream.toByteArray();
        }
    }
}
public class ApplicationConfig {

    public void getConfig(){
        System.out.println("这是我的配置...");
    }

}
public class HotDeploymentTest {
    public static void main(String[] args) throws Exception{
        Scanner sc = new Scanner(System.in);
        while(true){
            MyClassLoader loader = new MyClassLoader("E:\\Work\\classloader\\target\\classes");
            Class<?> aClass = loader.loadClass("cn.rwto.sample.ApplicationConfig");
            Object applicationConfig = aClass.newInstance();

            Method method = aClass.getMethod("getConfig");
            method.invoke(applicationConfig);
            
            Thread.sleep(5000);
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在不关闭进程的情况下,修改代码,重新编译,控制台发现 打印的内容跟着改变,热部署实现完毕!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1170498.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用maven的dependency插件分析工程的依赖

dependency:analyze https://maven.apache.org/plugins/maven-dependency-plugin/analyze-mojo.html 分析项目的依赖&#xff0c;确定哪些&#xff1a;用了并且声明了、用了但没有声明、没有使用但声明了。 dependency:analyze可以单独使用&#xff0c;所以它总是会执行test-…

【算法练习Day38】零钱兑换完全平方数

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 零钱兑换完全平方数总结&am…

软件测试:单元测试、集成测试、系统测试详解

实际的测试工作当中&#xff0c;我们会从不同的角度对软件测试的活动进行分类&#xff0c;题主说的“单元测试&#xff0c;集成测试&#xff0c;系统测试”&#xff0c;是按照开发阶段进行测试活动的划分。这种划分完整的分类&#xff0c;其实是分为四种“单元测试&#xff0c;…

TablePlus for Mac 数据库管理工具功能介绍

TablePlus是一款功能强大的数据库管理工具&#xff0c;专为Mac操作系统设计。它支持多种主流数据库&#xff0c;包括MySQL&#xff0c;PostgreSQL&#xff0c;SQLite&#xff0c;Microsoft SQL Server&#xff0c;Amazon Redshift等等。无论您是开发人员、数据库管理员还是数据…

【2024最新】Android Debug Bridge【下载安装】零基础到大神【附下载链接】

一、ADB简介 1、什么是adb ADB 全称为 Android Debug Bridge&#xff0c;起到调试桥的作用&#xff0c;是一个客户端-服务器端程序。其中客户端是用来操作的电脑&#xff0c;服务端是 Android 设备。 ADB 也是 Android SDK 中的一个工具&#xff0c;可以直接操作管理 Androi…

多个PDF发票合并实现一张A4纸打印2张电子/数电发票功能

python教程79--A4纸增值税电子发票合并打印_python 打印 发票设置_颐街的博客-CSDN博客文章浏览阅读7.9k次。接上篇https://blog.csdn.net/itmsn/article/details/121902974?spm1001.2014.3001.5501一张A4纸上下2张增值税电子发票实现办法。使用环境&#xff1a;python3.8、ma…

【C++基础知识学习笔记】精华版(复习专用)

常用语法 函数重载(Overload) 规则: 函数名相同 参数个数不同、参数类型不同、参数顺序不同 注意: 返回值类型与函数重载无关 调用函数时,实参的隐式类型转换可能会产生二义性 默认参数 C++ 允许函数设置默认参数,在调用时可以根据情况省略实参。规则如下: 默认参数只能…

chinese_llama_aplaca训练和代码分析

训练细节 ymcui/Chinese-LLaMA-Alpaca Wiki GitHub中文LLaMA&Alpaca大语言模型本地CPU/GPU训练部署 (Chinese LLaMA & Alpaca LLMs) - 训练细节 ymcui/Chinese-LLaMA-Alpaca Wikihttps://github.com/ymcui/Chinese-LLaMA-Alpaca/wiki/%E8%AE%AD%E7%BB%83%E7%BB%86%E…

selenium自动化测试入门 —— 获取元素对象!

一、元素定位简介 八种属性定位页面元素&#xff1a; By.ID By.XPATH By.LINK_TEXT By.PARTIAL_LINK_TEXT By.NAME By.TAG_NAME By.CLASS_NAME By.CSS_SELECTOR webdriver元素定位方法&#xff1a; driver.find_element(By.XXX,元素属性) # 定位单个元素 driver.find_elemen…

按键精灵中常用的命令

1. 声明变量&#xff1a; Dim 2. 注释语句 (1). 单行注释&#xff1a;这是一行注释&#xff0c;使用一个单引号进行注释&#xff1b; (2). 单行注释&#xff1a;// 这是一行注释&#xff0c;使用一对反斜杠进行注释; (3). 多行注释&#xff1a;/*这是多行注释&#xff0c;中…

canal+es+kibana+springboot

1、环境准备 服务器&#xff1a;Centos7 Jdk版本&#xff1a;1.8 Mysql版本&#xff1a;5.7.44 Canal版本&#xff1a;1.17 Es版本&#xff1a;7.12.1 kibana版本&#xff1a;7.12.1 软件包下载地址&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1jRpCJP0-hr9aI…

基于野狗算法的无人机航迹规划-附代码

基于野狗算法的无人机航迹规划 文章目录 基于野狗算法的无人机航迹规划1.野狗搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用野狗算法来优化无人机航迹规划。 1.野狗搜索算法 …

前端面试题整理(一)

前言&#xff1a; 这篇博客是记录自己在看面试过程中还未完全掌握的前端知识点&#xff0c;也是一些前端面试需要掌握的知识点&#xff08;总结的并不全面&#xff0c;可以参考&#xff0c;具体情况以自己实际为准&#xff09;,并且这篇博客正在持续更新中… 附言&#xff1a…

2023.11.4-Envoy使用案例-oss

2023.11.4-Envoy使用案例 目录 本节实战 实战名称&#x1f6a9; 实战&#xff1a;前端代理-2023.11.2(测试成功)&#x1f6a9; 实战&#xff1a;流量镜像-2023.11.4(测试成功)&#x1f6a9; 实战&#xff1a;故障注入过滤器-2023.11.4(测试成功)&#x1f6a9; 实战&#xff1a…

【Invea Therapeutics】申请7500万美元纳斯达克IPO上市

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;美国生物制药公司【Invea Therapeutics】近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市&#xff0c;股票代码为(INAI) &#xff0c;Invea …

基于龙格-库塔算法的无人机航迹规划-附代码

基于龙格-库塔算法的无人机航迹规划 文章目录 基于龙格-库塔算法的无人机航迹规划1.龙格-库塔搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用龙格-库塔算法来优化无人机航迹规划…

xlua源码分析(二)lua Call C#的无wrap实现

xlua源码分析&#xff08;二&#xff09;lua Call C#的无wrap实现 上一节我们主要分析了xlua中C# Call lua的实现思路&#xff0c;本节我们将根据Examples 03_UIEvent&#xff0c;分析lua Call C#的底层实现。例子场景里有一个简单的UI面板&#xff0c;面板中包含一个input fie…

多模态大模型最全综述

由微软7位华人研究员撰写--多模态基础模型已经从专用走向通用 它从目前已经完善的和还处于最前沿的两类多模态大模型研究方向出发&#xff0c;全面总结了五个具体研究主题&#xff1a; 视觉理解视觉生成统一视觉模型LLM加持的多模态大模型多模态agent 1、谁适合阅读这份综述&…

YOLOv8独家原创改进:自研独家创新BSAM注意力 ,基于CBAM升级

💡💡💡本文全网首发独家改进:提出新颖的注意力BSAM(BiLevel Spatial Attention Module),创新度极佳,适合科研创新,效果秒杀CBAM,Channel Attention+Spartial Attention升级为新颖的 BiLevel Attention+Spartial Attention 1)作为注意力BSAM使用; 推荐指数:…

时序预测 | MATLAB实现时间序列ACF和PACF分析

时序预测 | MATLAB实现时间序列ACF和PACF分析 目录 时序预测 | MATLAB实现时间序列ACF和PACF分析基本介绍程序设计参考资料基本介绍 自回归分析是线性回归分析的一种推广,主要是研究一个序列反映的自我因果关系。普通线性回归基于互相关分析,涉及两个以上的变量,一个作为因变…