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…...
跨境服务数字化转型 JAVA 国际版打手俱乐部陪玩系统完整开发教程
以下是基于JAVA开发国际版打手俱乐部陪玩系统的完整开发教程,涵盖技术选型、核心功能实现、安全合规及部署方案:一、技术选型与架构设计后端框架:Spring Boot 3.2 Spring Cloud Alibaba:提供微服务拆分能力,支持Nacos…...
模型timm/ViT-B-16-SigLIP简要介绍及其应用场景
目录一、timm/ViT-B-16-SigLIP 是什么模型二、模型结构(核心架构)1️⃣ 图像编码器2️⃣ 文本编码器3️⃣ 对齐训练三、为什么叫 ViT-B-16四、在 timm 中如何使用五、典型应用场景1️⃣ Zero-shot 图像分类2️⃣ 图文检索(Image-Text Retriev…...
当Logo消失,品牌资产还剩多少?
这个问题问得直接——品牌费尽心思把Logo放大、放正、放在C位,可如果有一天消费者真的“看不见”它,品牌还剩下什么?答案取决于品牌建设的本质:是在做识别符号,还是在做价值沉淀。1. 认知资产:剩不下什么Lo…...
PostgreSQL(OpenGauss/MogDB) 大小写转换实战:批量处理表名与字段名的自动化方案
1. 为什么PostgreSQL的大小写问题让人头疼? 第一次用PostgreSQL的时候,我就被它的大小写规则坑惨了。明明在Oracle里运行好好的SQL语句,搬到PostgreSQL就报"relation does not exist"错误。后来才发现,原来PostgreSQL对…...
hgproxy偶发性无法连接
文章目录环境症状问题原因解决方案环境 系统平台:银河麒麟 (鲲鹏) 版本:4.5.8 症状 hgproxy 4.0.33.3 出现偶发性无法连接现象,经过几分钟或几十秒或更长时间会自动恢复正常;psql 连接数据库端口正常&am…...
打造 TC397 AUTOSAR OS 多核工程最小系统:点亮多核的明灯之旅
tc397autosar os多核工程最小系统 tc397 autosar os 多核最小系统、配置工程、tasking工程 实现功能:六核跑起来、亮灯。在汽车电子领域,多核处理器的应用愈发广泛,TC397 凭借其强大的性能成为众多开发者的心头好。今天咱们就来聊聊如何搭建 …...
WiFi信号弱?5分钟搞懂dBi、dBm和dB的区别,选对天线不踩坑
WiFi信号弱?5分钟搞懂dBi、dBm和dB的区别,选对天线不踩坑 每次视频会议卡成PPT,游戏延迟飙红,或是刷剧总在关键时刻转圈——这些糟心体验八成是WiFi信号在作祟。很多人第一反应是升级千兆宽带,却忽略了无线信号从路由器…...
面向游戏开发者的UE4SS工具效能提升指南
面向游戏开发者的UE4SS工具效能提升指南 【免费下载链接】RE-UE4SS Injectable LUA scripting system, SDK generator, live property editor and other dumping utilities for UE4/5 games 项目地址: https://gitcode.com/gh_mirrors/re/RE-UE4SS 一、价值定位…...
React篇——第一章 React的基础知识(上篇)
目录 1. React简介 1.1 什么是React 1.2 React的核心优势 组件化开发 虚拟DOM 丰富的生态系统 跨平台支持 1.3 React的市场地位 2. 开发环境搭建 2.1 使用create-react-app创建项目 2.2 其他创建React项目的方式 3. JSX基础 3.1 什么是JSX 3.2 JSX的优势 3.3 JS…...
手把手教你用Python实现双足机器人ZMP预观控制(附开源代码)
用Python实现双足机器人ZMP预观控制的完整指南 1. ZMP理论基础与机器人动力学模型 零力矩点(ZMP)理论是现代双足机器人步态规划的核心概念,它定义了地面反作用力合力作用点的位置。当ZMP位于支撑多边形(由机器人足底接触点构成的凸多边形)内时,机器人能保…...
