深入分析ClassLocader工作机制
文章目录
- 一、ClassLoader简介
- 1. 概念
- 2. ClassLoader类结构分析
- 二、ClassLoader的双亲委派机制
- 三、Class文件的加载流程
- 1. 简介
- 2. 加载字节码到内存
- 3. 验证与解析
- 4. 初始化Class对象
- 四、常见加载类错误分析
- 1. ClassNotFoundException
- 2. NoClassDefFoundError
- 3. UnsatisfiledLinkError
- 4. ClassCastException
- 5. ExceptionInInitializerError
- 五、自定义ClassLoader的优势
一、ClassLoader简介
1. 概念
ClassLoader顾名思义就是类加载器,负责将Class加载到JVM中。事实上,ClassLoader除了能够将Class加载到JVM意外以外,还有一个重要的作用就是审查每个类应该由谁加载,它是一种父优先的等级加载机制。此外,ClassLoader除了上述的两个作用外还有一个任务就是将Class字节码重新解析成JVM统一要求的对象格式。
2. ClassLoader类结构分析
我们用到ClassLoader时常用下面的几个方法,以及它们的重载方法:
public abstract class ClassLoader {ClassLoader;Class<?> defineClass(byte[],int,int);Class<?> findClass(String);Class<?> loadClass(String);void resolveClass(Class<?>);
}
defineClass
方法用来将byte字节流解析成JVM能够识别的Class对象,有了这个方法我们不仅仅可以通过class文件实例化对象,还可以通过其他方式如我们通过网络接收一个类的字节码,拿这个字节码流直接创建类的Class对象形式实例化对象。defineClass
通常是和findClass
方法一起使用的,我们通过直接覆盖ClassLoader父类的findClass
方法来实现类的加载机制,从而取得想要加载类的字节码。然后调用defineClass
方法生成类的Class对象,如果你想在类被加载到JVM中时就被链接,那么可以调用另一个resolveClass
方法,当然你也可以选择让JVM来解决什么时候才链接到这个类。
如果你不想重新定义加载类的规则,只想在运行时能够加载自己指定的一个类而已,那么你可以用this.getClass().getClassLoader().loadClass("class")
调用ClassLoader的loadclass方法可以获取这个类的Class对象,这个loadClass还有重载方法,你统一可以决定在上面时候解析这个类。
二、ClassLoader的双亲委派机制
双亲委派机制(Parent Delegation Mechanism)是Java中的一种类加载机制。在Java中,类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时,它会先将该请求委派给它的父类加载器去尝试加载。只有当父类加载器无法加载该类时,子类加载器才会尝试加载。
这种机制的设计目的是为了保证类的加载是有序的,避免重复加载同一个类。Java中的类加载器形成了一个层次结构,根加载器(Bootstrap ClassLoader)位于最顶层,它负责加载Java核心类库。其他加载器如扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader)都有各自的加载范围和职责。通过双亲委派机制,可以确保类在被加载时,先从上层的加载器开始查找,逐级向下,直到找到所需的类或者无法找到为止。
这种机制的好处是可以避免类的重复加载,提高了类加载的效率和安全性。同时,它也为Java提供了一种扩展机制,允许开发人员自定义类加载器,实现特定的加载策略。
其实Bootstrap ClassLoader并不属于JVM的类等级层次,因为BootStrap ClassLoader并没有遵守ClassLoader的加载规则,另外它并没有子类,ExtClassLoader的父类也不是Bootstrap ClassLoader,我们应用中能取到的顶层父类时ExtClassLoader。
ExtClassLoader和AppClassLoader都位于sun.misc.Launcher
类中,它们是Loucher类的内部类。ExtClassLoader和AppClassLoader都继承了URLClassLoader,而URLClassLoader又实现了抽象类ClassLoader,在创建Launcher对象时会首先创建ExtClassLoader,然后将ExtClassLoader作为父加载器创建AppClassLoader对象,而通过Launcher.getClassLoade()方法获取的ClassLoader就是AppClassLoader对象。所以如果Java应用中没有定义其他ClassLoader,那么除了System.getProperty("java.ext.dirs")
目录下的类是由ExtClassLoader加载为,其它类都由AppClassLoader来加载。
JVM加载class文件到内存中有两种方式:
- 隐式加载:所谓隐式加载是不通过在代码里面调用ClassLoader来加载所需要的类,而是铜鼓oJVM来自动加载所需的类到内存的方式。例如:当我们在类中继承或者引用某个类是,JVM在解析当前这个类时发现引用不在内存中,那么自动将这些类加载到内存中。
- 显式加载:相反的显式加载就是我们在代码中使用ClassLoader类加载一个类的方式
其实这两种方式是混合使用的,例如我们通过自定义的ClassLoader显式加载一个类时,这个类又引用了其他类,那么这些类就是隐式加载的。
三、Class文件的加载流程
1. 简介
下面分析如何将class文件加载到JVM中。ClassLoader加载一个class文件到JVM要经历如下阶段:
- 首先找到class文件并把这个文件包含的字节码加载到内存中
- 链接阶段分为三个步骤,分别是字节码验证、Class类数据结构分析及相应的内存分配和最后符号表的链接
- 最后是类中静态数据和初始化赋值,以及静态块的执行
2. 加载字节码到内存
findClass()的方法是在ClassLoader实现类中实现的,例如URLClassLoader就实现了该方法,URLClassLoader类通过一个URLClassPath类的帮助取得要加载的class文件字节流,而这个URLClassPath定义了到哪里去找这个class文件,如果找到了这个class文件,再读取它的byte字节流通过调用defineClass()方法创建类对象。
private final URLClassPath ucp;
再看其构造函数,要指定一个URL数据才能创建URLClassLoader对象,也就是必须要指定这个ClassLoader默认到哪个目录中去查找class文件
public URLClassLoader(URL[] urls, ClassLoader parent) {super(parent);SecurityManager security = System.getSecurityManager();if (security != null) {security.checkCreateClassLoader();}this.acc = AccessController.getContext();ucp = new URLClassPath(urls, acc);}
在创建URLClassLoader对象时就根据传过来的URL数组中的路径来判断是文件还是jar包,根据路径不同分别创建FileLoader或者JarLoader,或者使用默认的加载器,当JVM调用findClass时由这几个加载器来将class文件加载到内存中。
3. 验证与解析
- 字节码验证,类装入器对于类的字节码要做许多检测,以确保格式正确、行为争取
- 类准备,这个阶段准备代表的每个类中定义的字段、方法和实现接口所必需的数据结构
- 解析,在这个阶段类装入器装入类所引用的其他类。可以用许多方式引用类,如超类、结构、字段、方法签名、方法中使用的本地变量
4. 初始化Class对象
类中包含的静态初始化器都被执行,在这一阶段末尾静态字段被初始化默认值。
四、常见加载类错误分析
在执行Java程序时经常会碰到ClassNotFoundException
和NoClassDefFoundError
两个异常,它们都与类加载有关,下面分析一下产生这些异常的原因:
1. ClassNotFoundException
这个异常通常发生在显示加载类的时候,例如,用如下方式调用加载一个类时就报了这个错:
public class Main {public static void main(String[] args) throws ClassNotFoundException {Class.forName("Jack");}
}
出现这个错误的原因是,JVM要加载指定的文件的字节码到内存时,并没有找到这个文件对应的字节码,也就是这个文件并不存在(在当前classpath目录下)。
获取classpath路径的方法:
this.getClass().getClassLoader().getResource("").toString()
2. NoClassDefFoundError
这个异常在第一次使用命令执行Java类时很可能会碰到,出现这种异常的可能原因是使用new关键字、属性引用某个类、继承了某个接口或类,以及方法的某个参数引用类某个类,这时会触发JVM的隐时加载这些类时发现这些类不存在。解决这个错误的方法就是确保每个类的引用的类都在当前的classpath下面。
3. UnsatisfiledLinkError
这个异常通常是JVM启动时,如果一不小心将JVM中的某个lib删除了,就可能会报这个错误。
public class Main {public native void nativeMethod();static {System.loadLibrary("Nolib");}public static void main(String[] args) throws ClassNotFoundException {new Main().nativeMethod();}
}
上面就是在解析native标识的方法时JVM找不到对应的本机库文件出现。
4. ClassCastException
这个错误比较常见,通常在程序中出现强制类型转换时出现这个错误。JVM在做类型转换时会按照如下规则进行检查:
- 对于普通对象,对象必须是目标类的实例或目标类的子类实例。如果目标类是一个接口,那么会把它当作实现该接口的一个字类。
- 对于数组类型,目标类必须是数组类型或java.lang.Objecgt、java.lang.CLoneable、java.io.Serializable
如果不满足上面规则,JVM就会报这个错误。要避免这个错误有两种方式:
- 在容器类型中显示地指明这个容器所包含的对象类型
- 先通过instanceof检查是不是目标类型,然后再进行强制类型转换
5. ExceptionInInitializerError
这个错误JVM规范中是这样定义的:
- 如果Java虚拟机试图创建类ExceptionInInitializerError的新实例,但是因为出现Out-Of-Memory-Error而无法创建新实例,那么就会抛出OutOfMemoryError对象作为代替
- 如果初始化器抛出一些Exception,而且Exception类不是Error或者它的某个子类,那么就会创建ExceptionInInitializerError类的一个新实例,并用Exception作为参数,用这个实例代替Exception
认值。
五、自定义ClassLoader的优势
通过前面的分析,ClassLoader能够完成的事情无非以下几种情况:
- 在自定义路径下查找自定义的class文件,也许我们需要的class文件并不总是在已经设置好的ClassPath下面,那么我们必须想办法找到这个类,在这种情况下我们需要自己实现一个ClassLoader
- 对我们自己的要加载的类做特殊处理,如保证通过网络传输的类的安全性,可以将类经过加密后再传输,在加载到JVM之前需要对类的字节码再解密,这个过程就可以在自定义的ClassLoader中实现
- 可以定义类的实效机制,如果我们可以检查已经加载的class文件是否修改,如果修改类可以重新加载这个类,从而实现类的热部署。
相关文章:
深入分析ClassLocader工作机制
文章目录 一、ClassLoader简介1. 概念2. ClassLoader类结构分析 二、ClassLoader的双亲委派机制三、Class文件的加载流程1. 简介2. 加载字节码到内存3. 验证与解析4. 初始化Class对象 四、常见加载类错误分析1. ClassNotFoundException2. NoClassDefFoundError3. UnsatisfiledL…...
算法通关村第十二关—字符串转换(青铜)
一、转换成小写字母 LeetCode709.给你一个字符串s,将该字符串中的大写字母转换成相同的小写字母,返回新的字符串。 示例1: 输入:s"Hello" 输出:"hello" 示例2: 输入:s&qu…...
C#基础与进阶扩展合集-基础篇(持续更新)
目录 本文分两篇,进阶篇点击:C#基础与进阶扩展合集-进阶篇 一、基础入门 Ⅰ 关键字 Ⅱ 特性 Ⅲ 常见异常 Ⅳ 基础扩展 1、哈希表 2、扩展方法 3、自定义集合与索引器 4、迭代器与分部类 5、yield return 6、注册表 7、不安全代码 8、方法…...
ReactJs笔记摘录
文章目录 前言目录结构组件动态组件高阶组件 Hook函数useStateuseEffectuseContextuseReduceruseCallbackuseMemo JSX语法根元素与斜杠使用变量推荐使用className替代class属性写法三元表达式 vs &&antd和tailwindcss 组件通信父传子:props和自定义函数事件…...
2023 re:Invent使用 PartyRock 和 Amazon Bedrock 安全高效构建 AI 应用程序
前言 本篇文章授权活动官方亚马逊云科技文章转发、改写权,包括不限于在 亚马逊云科技开发者社区, 知乎,自媒体平台,第三方开发者媒体等亚马逊云科技官方渠道 “Your Data, Your AI, Your Future.(你的数据,你的AI&…...
Mac 打不开github解决方案
序言 github 时有打不开的情况,为此很是烦恼,这里分享一下如何解决这种问题,其实问题的本质是在访问github网页时无法通过github.com的二级域名进行动态域名解析。 解决方案 手动配置静态文件hosts,将该域名和IP的映射关系添加…...
十五 动手学深度学习v2计算机视觉 ——全连接神经网络FCN
文章目录 FCN FCN 全卷积网络先使用卷积神经网络抽取图像特征,然后通过卷积层将通道数变换为类别个数,最后通过转置卷积层将特征图的高和宽变换为输入图像的尺寸。 因此,模型输出与输入图像的高和宽相同,且最终输出通道包含了该空…...
elementUI中的 “this.$confirm“ 基本用法,“this.$confirm“ 调换 “确认“、“取消“ 按钮的位置
文章目录 前言具体操作总结 前言 elementUI中的 "this.$confirm" 基本用法,"this.$confirm" 调换 "确认"、"取消" 按钮的位置 具体操作 基本用法 <script> this.$confirm(这是数据(res.data࿰…...
K8S 常用命令
获取所有的pod资源: kubectl get pod 获取所有的命名空间: kubectl get namespace 获取所有的Deployment资源: kubectl get deployment 删除指定的deploy: kubectl delete deploy nginx 获取所有的服务: kubectl get serv…...
12.使用 Redis 优化登陆模块
目录 1. 使用 Redis 优化登陆模块 1.1 使用 Redis 存储验证码 1.2 使用 Redis 存储登录凭证 1.3 使用 Redis 缓存用户信息 1. 使用 Redis 优化登陆模块 使用 Redis 存储验证码:验证码需要频繁的访问与刷新,对性能要求较高;验证码不需要永…...
Nacos-NacosRule 负载均衡—设置集群使本地服务优先访问
userservice: ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则 NacosRule 权重计算方法 目录 一、介绍 二、示例(案例截图) 三、总结 一、介绍 NacosRule是AlibabaNacos自己实现的一个负载均衡策略&…...
软件设计师——信息安全(二)
📑前言 本文主要是【信息安全】——软件设计师——信息安全的文章,如果有什么需要改进的地方还请大佬指出⛺️ 🎬作者简介:大家好,我是听风与他🥇 ☁️博客首页:CSDN主页听风与他 🌄…...
Unity中实现ShaderToy卡通火(原理实现篇)
文章目录 前言一、我们在片元着色器中,实现卡通火的大体框架1、使用 noise 和 _CUTOFF 判断作为显示火焰的区域2、_CUTOFF : 用于裁剪噪波范围的三角形3、noise getNoise(uv, t); : 噪波函数 二、顺着大体框架依次解析具体实现的功能1、 uv.x * 4.0; : …...
引迈信息-JNPF平台怎么样?值得入手吗?
目录 1.前言 2.引迈低代码怎么样? 3.平台亮点展示 4.引迈产品特点 5.引迈产品技术栈: 1.前言 低代码是近几年比较火的一种应用程序快速开发方式,它能帮助用户在开发软件的过程中大幅减少手工编码量,并通过可视化组件加速应用…...
大数据云计算——使用Prometheus-Operator进行K8s集群监控
大数据云计算——使用Prometheus-Operator进行K8s集群监控 一、 背景 在非operator配置的普罗中我们监控k8s集群都是通过配置configmap进行服务发现和指标拉取。切换到prometheus-operator难免会有些使用问题。不少用户已经习惯底层配置自动发现的方式。当过渡到servicemonit…...
[蓝桥杯刷题]合并区间、最长不连续子序列、最长不重复数组长度
前言 ⭐Hello!这里是欧_aita的博客。 ⭐今日语录: 成功的关键在于对目标的持久追求。 ⭐个人主页:欧_aita ψ(._. )>⭐个人专栏: 数据结构与算法 数据库 文章目录 前言合并区间问题📕现实应用大致思路代码实现代码讲解 最长不连续子序列&a…...
Hazel引擎学习(十二)
我自己维护引擎的github地址在这里,里面加了不少注释,有需要的可以看看 参考视频链接在这里 这是这个系列的最后一篇文章,Cherno也基本停止了Games Engine视频的更新,感觉也差不多了,后续可以基于此项目开发自己想要…...
中文字符串逆序输出
今天碰到这个题,让我逆序输出中文字符串,可给我烦死了,之前没有遇到过,也是查了资料才知道,让我太汗颜了。 英文字符串逆序输出很容易,开辟一块空间用来存放逆序后的字符串,从后往前遍历原字符串…...
MySQL BinLog 数据还原恢复
博文目录 文章目录 查看状态查看 binlog 开关及存储路径查看 binlog 配置 如 存储格式 binlog_format查看当前还存在的日志查看当前正在使用的日志 切换日志确定日志确定日志文件日志格式改写日志简要说明确定日志位置以事件为单位查看日志分析日志 还原数据 查看状态 查看 b…...
理想汽车校招内推--大量hc等你来
投递链接: https://li.jobs.feishu.cn/s/i8BLJE1j 欢迎大家投递...
RabbitMQ死信队列详解
什么是死信队列 由于特定的**原因导致 Queue 中的某些消息无法被消费,**这类消费异常的数据将会保存在死信队列中防止消息丢失,例如用户在商城下单成功并点击支付后,在指定时间未支付时的订单自动失效死信队列只不过是绑定在死信交换机上的队…...
计算机网络:物理层(编码与调制)
今天又学会了一个知识,加油! 目录 一、基带信号与宽带信号 1、基带信号 2、宽带信号 3、选择 4、关系 二、数字数据编码为数字信号 1、非归零编码【NRZ】 2、曼彻斯特编码 3、差分曼彻斯特编码 4、归零编码【RZ】 5、反向不归零编码【NRZI】 …...
嵌入式开发板qt gdb调试
1) 启动 gdbserver ssh 或者 telnet 登陆扬创平板 192.168.0.253, 进入命令行执行如下: chmod 777 /home/HelloWorld (2) 打 开 QTcreator->Debug->StartDebugging->Attach to Running Debug Server 进行…...
基于python实现原神那维莱特开转脚本
相信不少原友都抽取了枫丹大C那维莱特,其强力的输出让不少玩家爱不释手。由于其转的越快,越不容易丢伤害的特点,很多原友在开转时容易汗流浃背,所以特意用python写了一个自动转圈脚本,当按住鼠标侧键时,即可…...
C# 实现Lru缓存
C# 实现Lru缓存 LRU 算法全称是最近最少使用算法(Least Recently Use),是一种简单的缓存策略。 通常用在对象池等需要频繁获取但是又需要释放不用的地方。 代码实现的基本原理就是使用链表,当某个元素被访问时(Get或…...
牛客网BC107矩阵转置
答案: #include <stdio.h> int main() {int n0, m0,i0,j0,a0,b0;int arr1[10][10]{0},arr2[10][10]{0}; //第一个数组用来储存原矩阵,第二个数组用来储存转置矩阵scanf("%d%d",&n,&m); if((n>1&&n<10)&&am…...
协作办公原来如此简单?详解 ONLYOFFICE 协作空间 2.0 更新
协作办公原来如此简单?详解 ONLYOFFICE 协作空间 2.0 更新 上周,ONLYOFFICE 的协作空间推出升级版 2.0 版本了: ONLYOFFICE 协作空间 2.0 现已发布:新增公共房间、插件、重新分配数据、RTL 界面等功能 ONLYOFFICE 协作空间是去…...
2023年国赛高教杯数学建模A题定日镜场的优化设计解题全过程文档及程序
2023年国赛高教杯数学建模 A题 定日镜场的优化设计 原题再现 构建以新能源为主体的新型电力系统,是我国实现“碳达峰”“碳中和”目标的一项重要措施。塔式太阳能光热发电是一种低碳环保的新型清洁能源技术[1]。 定日镜是塔式太阳能光热发电站(以下…...
c/c++ 结构体、联合体、枚举
结构体 结构体内存对齐规则: 1、结构体的第一个成员对齐到结构体变量起始位置偏移量为0的地址处 2、其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数:编译器默认的一个对齐数与该成员变量大小的较小值。 vs 中…...
stl模板库成员函数重载类型混肴编译不通过解决方法
stl模板库成员函数重载类型混肴编译不通过解决方法 这种方式编译不通过IsArithmetic和HasMemberList编译器存在混肴 template <typename T, typename Enable std::enable_if<IsArithmetic<T>::value>::type >static void DumpWrapper(T* filed, std::strin…...
送上门卤菜网站要怎么做/常用的网络营销方法
1. 文档介绍 1.1. 为什么要写这个文档 接触Spring和MyBatis也挺久的了,但是一直还停留在使用的层面上,导致很多时候光知道怎么用,而不知道其具体原理,这样就很难做一些针对性的优化工作,Spring和MyBatis都已…...
网站 模板/免费seo教程分享
要解决Oracle的客户端乱码问题关键是要把服务器端使用的字符集跟客户端使用的字符集统一起来。Oracle客户端(Sqlplus)通过NLS_LANG环境变量来确定客户端使用的字符集。NLS_LANG参数由以下部分组成:NLS_LANG<Language>_<Territory>.<Clien…...
显示官网字样的网站怎么做/新闻发布平台有哪些
一. Hive操作HQL语句的两个参数 假设有两个hql语句A和B,hql语句A要求在凌晨5点执行,B语句要求在A执行完之后执行,那么我们怎么做呢? 1.通过人工在hive客户端执行(不可行) 2.写一个Java程序,使用JDBC定时执行这样的操作…...
江苏省建设厅网站首页/百度怎么推广自己的信息
head 与 tail 就像它的名字一样的浅显易懂,它是用来显示开头或结尾某个数量的文字区块,head 用来显示档案的开头至标准输出中,而 tail 想当然尔就是看档案的结尾。 1.命令格式: head [参数]... [文件]... 2…...
专业广州网站设计/安卓aso优化工具
python在ros中订阅雷达数据并可视化处理 文章目录python在ros中订阅雷达数据并可视化处理参考教程:预设条件:图片:程序:接着上一次的博客,我可视化激光雷达的数据还是整好了,记录一下吧,国内现成…...
电影网站设计模板/专业搜索引擎seo公司
编程新手猜数字游戏 #include <stdio.h> #include <time.h> #include<stdlib.h> void main() { srand(time(0)); int n rand() % 100 1,c 0,a 0; printf("我有一个1-100的随机数,你猜是多少:>"); do { scanf("%d", &…...