Java类加载机制(类加载器,双亲委派模型,热部署示例)
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的角度来看,一共有两种类加载器,BootStrapClassLoader 和 java 自定义的类加载器
其中 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)
- 是类方法
- 默认使用系统类加载器进行加载
- 加载一个指定的类,会对类进行初始化,执行类中的静态代码块,以及对静态变量的赋值等操作, 一般用于加载驱动,例如jdbc驱动
public Class<?> loadClass(String name)
- 是成员方法
- 懒加载,只是加载,不会解析更不会初始化所反射的类
双亲委派模型
rents Delegation Model
注意:双亲并不是指存在父母两个类加载器,实际只有一个parent 父加载器 并且是作为加载器的属性,而不是继承,可以理解为 雌雄同株

双亲委派 类加载流程
- 当一个类开始进行加载时,会先从判断这个类是否已经被加载了,如果已经加载了,返回已加载的类Class对象
- 如果还没有被加载,通过parent属性 将加载请求传递给上层类加载器进行加载
- 一直调用到扩展类加载器(ExtClassLoader),如果都没有找到已经被加载的Class对象,此时parent == null ,通过 findBootstrapClassOrNull() 方法传递给 BootStrapClassLoader 进行类加载
- 如果BootStrapClassLoader 没有加载成功,其下层类加载器开始尝试进行加载
- 如果一直到最底层的类加载器(用户自定义的类加载器)都没有加载成功,则抛出ClassNotFoundException异常
一句话概括:子类加载器调用父类加载器去加载类
源码如下:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> 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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}
优缺点
优点:
- 防止重复加载,确保一个类的全局唯一性
当一个类加载器要加载一个类,首先交给父类加载器进行加载,并且加载过程由syncronized锁控制。避免重复加载同一个类
- 确保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*/@Overrideprotected 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*/@Overridepublic 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);}}
}


在不关闭进程的情况下,修改代码,重新编译,控制台发现 打印的内容跟着改变,热部署实现完毕!
相关文章:
Java类加载机制(类加载器,双亲委派模型,热部署示例)
Java类加载机制 类加载器类加载器的执行流程类加载器的种类加载器之间的关系ClassLoader 的主要方法Class.forName()与ClassLoader.loadClass()区别 双亲委派模型双亲委派 类加载流程优缺点 热部署简单示例 类加载器 类加载器的执行流程 类加载器的种类 AppClassLoader 应用类…...
【C语言初学者周冲刺计划】3.2将一个数组中的值逆序重新存放
目录 1解题思路: 2代码 3运行代码如图: 4总结: 1解题思路: 首先学会如何利用循环输入位数和输入数值,然后再利用循环逆序即可 2代码 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { int…...
【C++心愿便利店】No.11---C++之string语法指南
文章目录 前言一、 为什么学习string类二、标准库中的string类 前言 👧个人主页:小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C 心愿便利店 🔑本章内容:str…...
OpenCV检测圆(Python版本)
文章目录 示例代码示例结果调参 示例代码 import cv2 import numpy as np# 加载图像 image_path DistanceComparison/test_image/1.png image cv2.imread(image_path, cv2.IMREAD_COLOR)# 将图像转换为灰度 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 使用高斯模糊消除…...
轻量封装WebGPU渲染系统示例<15>- DrawInstance批量绘制(源码)
当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/main/src/voxgpu/sample/DrawInstanceTest.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 细节请见:引擎系统设计思路 - 用户态与系统态隔离-CSDN博客 2. 高频调用与低频调用隔离。…...
E: 仓库 “http://cn.archive.ubuntu.com/ubuntu kinetic Release” 没有 Release 文件。
sudo apt-get update时报以下错误: E: 仓库 “http://cn.archive.ubuntu.com/ubuntu kinetic Release” 没有 Release 文件。 N: 无法安全地用该源进行更新,所以默认禁用该源。 N: 参见 apt-secure(8) 手册以了解仓库创建和用户配置方面的细节。 E: 仓库…...
【VR开发】【Unity】【VRTK】3-VR项目设置
任何VR避不开的步骤 如何设置VR项目,无论是PC VR还是安卓VR,我在不同的系列教程中都说过了,不过作为任何一个VR开发教程都难以避免的一环,本篇作为VRTK的开发教程还是对VR项目设置交代一下。 准备好你的硬件 头盔必须是6DoF的,推荐Oculus Quest系列,Rift系列,HTC和Pi…...
git log 用法
git log --format"%s" -n 1在 Git 中,您可以使用 git log 命令来查看提交历史,其中包含每个提交的详细信息,包括提交消息。如果您只想提取提交信息而不是完整的 git log 输出,可以使用 git log 命令的 --format 选项来指…...
Linux学习---有关监控系统zabbix的感悟
监控系统 监控系统就像咱们日常生活中小区监控(Monitor),用于及时发现问题(PROBLEM),根据相应的规则可以触发警告(Media),在后台显示屏(Dashboard)上以某种方面显示出来,高级的报警系统也许还能实现电话通知等功能,目的是为及时发…...
apollo云实验:定速巡航场景仿真调试
定速巡航场景仿真调试 概述启动仿真环境仿真系统修改默认巡航速度 实验目的福利活动 主页传送门:📀 传送 概述 自动驾驶汽车在实现落地应用前,需要经历大量的道路测试来验证算法的可行性和系统的稳定性,但道路测试存在成本高昂、…...
基于RK3568的新能源储能能量管理系统ems
新能源储能能量管理系统(EMS)是一种基于现代化技术的系统,旨在管理并优化新能源储能设备的能量使用。 该系统通过监测、调度和控制新能源储能设备来确保能源的高效利用和可持续发展。 本文将从不同的角度介绍新能源储能能量管理系统的原理、…...
dockerfile避坑笔记(VMWare下使用Ubuntu在Ubuntu20.04基础镜像下docker打包多个go项目)
一、docker简介 docker是一种方便跨平台迁移应用的程序,通过docker可以实现在同一类操作系统中,如Ubuntu和RedHat两个linux操作系统中,实现程序的跨平台部署。比如我在Ubuntu中打包了一个go项目的docker镜像(镜像为二进制文件&am…...
Qt 使用QtXlsx操作Excel表
1.环境搭建 QtXlsx是一个用于读写Microsoft Excel文件(.xlsx)的Qt库。它提供了一组简单易用的API,可以方便地处理电子表格数据。 Github下载:GitHub - dbzhang800/QtXlsxWriter: .xlsx file reader and writer for Qt5 官方文档…...
canal+es+kibana+springboot
1、环境准备 服务器:Centos7 Jdk版本:1.8 Mysql版本:5.7.44 Canal版本:1.17 Es版本:7.12.1 kibana版本:7.12.1 软件包下载地址:链接:https://pan.baidu.com/s/1jRpCJP0-hr9aI…...
【力扣】面试经典150题——双指针
文章目录 125. 验证回文串392. 判断子序列167. 两数之和 II - 输入有序数组11. 盛最多水的容器15. 三数之和 125. 验证回文串 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字…...
6-8 最宽层次结点数 分数 10
文章目录 1.题目描述2.本题ac答案2.1法一: 代码复用2.2法二: 顺序队列实现层序遍历 3.C层序遍历求最大宽度3.1层序遍历代码3.2求最大宽度 1.题目描述 2.本题ac答案 2.1法一: 代码复用 //二叉树第i层结点个数 int LevelNodeCount(BiTree T, int i) {if (T NULL || i < 1)re…...
Linux学习第28天:Platform设备驱动开发(二): 专注与分散
Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 三、硬件原理图分析 四、驱动开发 1、platform设备与驱动程序开发 53 /* 54 * 设备资源信息,也就是 LED0 所使用的所有寄存器 55 */ 56 static str…...
postgresql数组重叠(有共同元素)查询
直接上最终代码: select distinct id from a where string_to_array(in_area,,) && (select ARRAY_AGG( code) from areas where code like 11% or code 100000)::TEXT[] pg语法: 表 9.48显示了可用于数组类型的运算符。 表 9.48。数组运算符…...
ubuntu系统 生成RSA密钥对
在Ubuntu系统上生成密钥对通常指的是生成SSH密钥对,它常用于安全的远程登录、数据通信和其他安全网络操作。以下是如何在Ubuntu系统上生成SSH密钥对的步骤: 打开终端:你可以使用快捷键 Ctrl Alt T 在Ubuntu上打开一个终端窗口。 运行ssh-k…...
【RtpSeqNumOnlyRefFinder】webrtc m98: ManageFrameInternal 的帧决策过程分析
Jitterbuffer(FrameBuffer)需要组帧以后GOP内的参考关系 JeffreyLau 大神分析 了组帧原理而参考关系(RtpFrameReferenceFinder)的生成伴随了帧决策 FrameDecisionFrameDecision 影响力 帧的缓存。调用 OnAssembledFrame 传递已经拿到的RtpFrameObject 那么,RtpFrameObject…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
Oracle11g安装包
Oracle 11g安装包 适用于windows系统,64位 下载路径 oracle 11g 安装包...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
