JVM之类加载器

文章目录

  • 版权声明
  • 类加载器
  • 类加载器的分类
    • 启动类加载器
    • 拓展类加载器&应用程序类加载器
  • 双亲委派机制
    • 解决三个问题
  • 打破双亲委派机制
    • 自定义类加载器
    • 案例演示
    • 线程上下文类加载器
    • 案例梳理
    • OSGi模块化

版权声明

  • 本博客的内容基于我个人学习黑马程序员课程的学习笔记整理而成。我特此声明,所有版权属于黑马程序员或相关权利人所有。本博客的目的仅为个人学习和交流之用,并非商业用途。
  • 我在整理学习笔记的过程中尽力确保准确性,但无法保证内容的完整性和时效性。本博客的内容可能会随着时间的推移而过时或需要更新。
  • 若您是黑马程序员或相关权利人,如有任何侵犯版权的地方,请您及时联系我,我将立即予以删除或进行必要的修改。
  • 对于其他读者,请在阅读本博客内容时保持遵守相关法律法规和道德准则,谨慎参考,并自行承担因此产生的风险和责任。本博客中的部分观点和意见仅代表我个人,不代表黑马程序员的立场。

类加载器

  • 类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。
  • 类加载器只参与加载过程中的字节码获取并加载到内存这一部分
    在这里插入图片描述
    在这里插入图片描述
  • 类加载器的应用场景
    在这里插入图片描述

类加载器的分类

  • 类加载器分为两类,一类是Java代码中实现的,一类是Java虚拟机底层源码实现的。
    -
  • 类加载器的设计JDK8和8之后的版本差别较大,JDK8及之前的版本中默认的类加载器有如下几种
    在这里插入图片描述
  • 类加载器的详细信息可以通过classloader命令查看:
    • classloader - 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去getResource
    • 详细的指令教程请参考Arthas官网
[arthas@17048]$ classloadername                                       numberOfInstances  loadedCountTotalBootstrapClassLoader                       1                  2248com.taobao.arthas.agent.ArthasClassloader  1                  1351sun.misc.Launcher$ExtClassLoader           1                  47sun.reflect.DelegatingClassLoader          15                 15sun.misc.Launcher$AppClassLoader           1                  8
Affect(row-cnt:5) cost in 7 ms.
[arthas@17048]$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@7fe21cf8+-com.taobao.arthas.agent.ArthasClassloader@53e59711+-sun.misc.Launcher$AppClassLoader@18b4aac2
Affect(row-cnt:4) cost in 5 ms.
  • BootstrapClassLoader:c++编写的启动类加载器,位于虚拟机的源码中
    • numberOfInstances :1 类加载器的个数为一个
    • loadedCountTotal: 2248 共加载了2248个核心类
  • ArthasClassloader: ArthasClassloader工具的加载器
  • ExtClassLoader:拓展类加载器
  • DelegatingClassLoader: jdk底层实现用来提升反射效率的加载器
  • AppClassLoader:应用程序类加载器

启动类加载器

  • 启动类加载器(Bootstrap ClassLoader)是由Hotspot虚拟机提供的、使用C++编写的类加载器
  • 默认加载Java安装目录/jre/lib下的类文件,比如rt.jar,tools.jar,resources.jar等。
    在这里插入图片描述
  • 通过启动类加载器去加载用户jar包
    • 放入jre/lib下进行扩展:不推荐,尽可能不要去更改JDK安装目录中的内容,会出现即时放进去由于文件名不匹配的问题也不会正常地被加载
    • 使用参数进行扩展:推荐,使用-Xbootclasspath/a:jar包目录/jar包名 进行扩展

拓展类加载器&应用程序类加载器

  • 扩展类加载器和应用程序类加载器都是JDK中提供的、使用Java编写的类加载器

  • 它们的源码都位于sun.misc.Launcher中,是一个静态内部类,继承自URLClassLoader。通过目录或者指定jar包将字节码文件加载到内存中。
    在这里插入图片描述

  • 扩展类加载器(Extension Class Loader)是JDK中提供的、使用Java编写的类加载器

  • 默认加载Java安装目录/jre/lib/ext下的类文件

在这里插入图片描述

  • 通过扩展类加载器去加载用户jar包
    • 放入/jre/lib/ext下进行扩展:不推荐,尽可能不要去更改JDK安装目录中的内容
  • 使用参数进行扩展
    • 推荐,使用-Djava.ext.dirs=jar包目录 进行扩展,这种方式会覆盖掉原始目录,可以用;(windows):(macos/linux)追加上原始目录
  • 类加载器的加载路径可以先使用classloader -l查看类的hash值,然后通过classloader –c hash值 查看
    在这里插入图片描述

双亲委派机制

  • 由于Java虚拟机中有多个类加载器,那么一个类到底由哪个加载器进行加载?
  • 解决方案:双亲委派机制 双亲委派机制的核心是解决一个类到底由谁加载的问题
    在这里插入图片描述
  • 每个Java实现的类加载器中保存了一个成员变量叫“父”(Parent)类加载器,可以理解为它的上级,并不是继承关系。
    在这里插入图片描述
  • 启动类加载器使用C++编写,没有上级类加载器
  • 应用程序类加载器的parent父类加载器是扩展类加载器,而扩展类加载器的parent是空。扩展类加载器(Extension ClassLoader)在类加载过程中会委托给启动类加载器(Bootstrap ClassLoader)。这是因为Java的类加载器遵循所谓的"双亲委派模型"。
图1 真实的父类关系
图2 双亲委派机制中的逻辑关系
  • 尽管在类加载器的层次结构中,扩展类加载器的父类加载器是启动类加载器,但实际上,在类的加载过程中,应用类加载器的父类加载器是扩展类加载器,扩展类加载器没有父类加载器,但会委派给启动类加载器加载。这就是通常说扩展类加载器的父类加载器是启动类加载器。
  • 类加载器的继承关系可以通过classloader –t 查看
  • 双亲委派机制:当一个类加载器收到类加载请求时,它并不会尝试立即加载这个类,而是把这个请求委派给父类加载器。这样,每个类加载请求都会传递到启动类加载器,只有当父类加载器无法处理这个请求时(例如,类不在它的搜索路径中),子类加载器才会尝试自己去加载。

  • 双亲委派机制口诀:自底向上查找是否加载过,再由顶向下进行加载。
    在这里插入图片描述
    在这里插入图片描述

请添加图片描述
在这里插入图片描述

解决三个问题

  1. 重复的类:如果一个类重复出现在三个类加载器的加载位置,应该由谁来加载?
    • 启动类加载器加载,根据双亲委派机制,它的优先级是最高的
  2. String类覆盖问题:在自己的项目中去创建一个java.lang.String类,会被加载吗?
    • 不能,会交由启动类加载器加载在rt.jar包中的String类
  3. 类加载器的关系:这几个类加载器彼此之间存在关系吗?
    • 应用类加载器的父类加载器是扩展类加载器,扩展类加载器没有父类加载器,但是会委派给启动类加载器加载

打破双亲委派机制

  • 打破双亲委派机制的三种方式
  1. 自定义类加载器
    • 自定义类加载器并且重写loadClass方法,就可以将双亲委派机制的代码去除
  2. 线程上下文类加载器
    • Tomcat通过这种方式实现应用之间类隔离
  3. Osgi框架的类加载器
    • 历史上Osgi框架实现了一套新的类加载器机制,允许同级之间委托进行类的加载

自定义类加载器

  • 一个Tomcat程序中是可以运行多个Web应用的,如果这两个应用中出现了相同限定名的类,比如Servlet类,Tomcat要保证这两个类都能加载并且它们应该是不同的类。
  • 如果不打破双亲委派机制,当应用类加载器加载Web应用1中的MyServlet之后,Web应用2中相同限定名的MyServlet类就无法被加载。
    在这里插入图片描述
  • Tomcat使用了自定义类加载器来实现应用之间类的隔离。每一个应用会有一个独立的类加载器加载对应的类。
    在这里插入图片描述

  1. 先来分析ClassLoader的原理,ClassLoader中包含了4个核心方法
  2. 双亲委派机制的核心代码就位于loadClass方法中
    在这里插入图片描述
public Class<?> loadClass(String name) throws ClassNotFoundException {return this.loadClass(name, false); //resolve
}protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {//加锁 保证只有一个线程加载类synchronized(this.getClassLoadingLock(name)) {//查询当前类加载器是否加载过需要的类,true 返回类对象 否则,返回nullClass<?> c = this.findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();//向逻辑父类委派加载任务try {//直到委派到顶级父类——Extension,进入else分支if (this.parent != null) {c = this.parent.loadClass(name, false);} else {//调用启动类加载器,进行加载,无法加载返回nullc = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException var10) {}//所有类加载器加载失败,使用本类加载器进行加载if (c == null) {long t1 = System.nanoTime();c = this.findClass(name);PerfCounter.getParentDelegationTime().addTime(t1 - t0);PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);PerfCounter.getFindClasses().increment();}}if (resolve) {this.resolveClass(c);}return c;}
}final Class<?> loadClass(Module module, String name) {synchronized(this.getClassLoadingLock(name)) {Class<?> c = this.findLoadedClass(name);if (c == null) {c = this.findClass(module.getName(), name);}return c != null && c.getModule() == module ? c : null;}
}
//读取二进制数据
protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);
}protected Class<?> findClass(String moduleName, String name) {if (moduleName == null) {try {return this.findClass(name);} catch (ClassNotFoundException var4) {}}return null;
}
//类名校验 将字节码信息加载到虚拟机内存中
protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError {return this.defineClass((String)null, b, off, len, (ProtectionDomain)null);
}protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError {return this.defineClass(name, b, off, len, (ProtectionDomain)null);
}protected final void resolveClass(Class<?> c) {if (c == null) {throw new NullPointerException();}
}
  • 阅读双亲委派机制的核心代码,分析如何通过自定义的类加载器打破双亲委派机制
  • 打破双亲委派机制的核心就是将下边这一段代码重新实现
//parent等于null说明父类加载器是启动类加载器,直接调用findBootstrapClassOrNull
//否则调用父类加载器的加载方法
if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);}
//父类加载器爱莫能助,我来加载!
if (c == null) c = findClass(name);

案例演示

public class CustomClassLoader extends ClassLoader {public CustomClassLoader(ClassLoader parent) {super(parent);}@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {// 简单的例子, 只处理自定义的类if (name.startsWith("com.mydomain")) {return findClass(name);}// 其他的类还是采用双亲委派机制加载return super.loadClass(name);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] bytes = loadClassData(name);return defineClass(name, bytes, 0, bytes.length);} catch (IOException e) {throw new ClassNotFoundException("Cannot load class " + name, e);}}private byte[] loadClassData(String name) throws IOException {// 根据类名加载类的二进制数据. 假设所有类都在一个固定的目录下Path path = Paths.get("classes", name.replace('.', '/') + ".class");return Files.readAllBytes(path);}}
  1. 自定义类加载器默认父类是AppClassLoader
  • 以Jdk8为例,ClassLoader类中提供了构造方法设置parent的内容
    在这里插入图片描述
  • 这个构造方法由另外一个构造方法调用,其中父类加载器由getSystemClassLoader方法设置,该方法返回的是AppClassLoader。
    在这里插入图片描述
  • 两个自定义类加载器加载相同限定名的类,不会冲突
    • 在同一个Java虚拟机中,只有相同类加载器+相同的类限定名才会被认为是同一个类

  • 正确的去实现一个自定义类加载器的方式是重写findClass方法,这样不会破坏双亲委派机制
    在这里插入图片描述

线程上下文类加载器

  • 利用上下文类加载器加载类,比如JDBC和JNDI等
  • JDBC中使用了DriverManager来管理项目中引入的不同数据库的驱动,比如mysql驱动、oracle驱动。
    在这里插入图片描述
  1. DriverManager类位于rt.jar包中,由启动类加载器加载
    在这里插入图片描述

  2. 依赖中的mysql驱动对应的类,由应用程序类加载器来加载。
    在这里插入图片描述

  • DriverManager属于rt.jar是启动类加载器加载的。而用户jar包中的驱动需要由应用类加载器加载,这就违反了双亲委派机制
    在这里插入图片描述
  • DriverManage使用SPI机制,最终加载jar包中对应的驱动类
    在这里插入图片描述
  • SPI中获取到应用程序类加载器
    • SPI中使用了线程上下文中保存的类加载器进行类的加载,这个类加载器一般是应用程序类加载器
      在这里插入图片描述

案例梳理

⚫1. 启动类加载器加载DriverManager。
⚫ 2. 在初始化DriverManager时,通过SPI机制加载jar包中的myql驱动。
⚫ 3. SPI中利用了线程上下文类加载器(应用程序类加载器)去加载类并创建对象。

  • 由启动类加载器加载的类,委派应用程序类加载器去加载类的方式,打破了双亲委派机制
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

OSGi模块化

  • OSGi模块化框架。它存在同级之间的类加载器的委托加载。OSGi还使用类加载器实现了热部署的功能。
  • 热部署指的是在服务不停止的情况下,动态地更新字节码文件到内存中
    在这里插入图片描述

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

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

相关文章

KVM虚拟机迁移原理与实践

虚拟机迁移 迁移(migration)包括系统整体的迁移和某个工作负载的迁移&#xff0c;系统整体迁移是将系统上的所有软件&#xff0c;包括操作系统&#xff0c;完全复制到另一台物理硬件机器上&#xff0c;而工作负载迁移仅仅迁移特定的工作负载。 虚拟化技术的出现&#xff0c;丰…

Linux学习第二枪(yum,vim,g++/gcc,makefile的使用)

前言&#xff1a;在我的上一篇Linux博客我已经讲了基础指令和权限&#xff0c;现在我们来学习如何在Linux上运行和执行代码 目录 一&#xff0c;yum 二&#xff0c;vim 1&#xff09;命令行模式 2&#xff09;插入模式 3&#xff09;底行模式 三&#xff0c;gcc/g 四&a…

MATLAB的编程与应用,匿名函数、嵌套函数、蒙特卡洛法的掌握与使用

目录 1.匿名函数 1.1.匿名函数的定义与分类 1.2.匿名函数在积分和优化中应用 2.嵌套函数 2.1.嵌套函数的定义与分类 2.2.嵌套函数彼此调用关系 2.3.嵌套函数在积分和微分中应用 3.微分和积分 4.蒙特卡洛法 4.1.圆周率的模拟 4.2.计算N重积分&#xff08;均匀分布&am…

计算机提示找不到xinput1_3.dll怎么办?6个xinput1_3.dll丢失完美解决方案分享

xinput1_3.dll是Windows操作系统中的一个重要动态链接库文件&#xff0c;它负责处理游戏控制器和其他输入设备的相关功能。当计算机出现xinput1_3.dll缺失的问题时&#xff0c;可能会导致无法正常使用游戏控制器或其他输入设备。下面是针对这个问题的6个解决方法&#xff1a; 方…

微服务-我对Spring Clound的理解

官网&#xff1a;https://spring.io/projects/spring-cloud 官方说法&#xff1a;Spring Cloud 为开发人员提供了快速构建分布式系统中一些常见模式的工具&#xff08;例如配置管理、服务发现、熔断器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话…

分布式搜索引擎ES

文章目录 初识elasticsearch了解ES倒排索引正向索引倒排索引正向和倒排 es的一些概念文档和字段索引和映射mysql与elasticsearch 安装ES部署kibana安装IK分词器扩展词词典停用词词典 索引库操作mapping映射属性索引库的CRUD创建索引库和映射查询索引库修改索引库删除索引库 文档…

ChatGPT 如何改变科研之路

《Nature》全球博士后调查[1]中约有三分之一的受访者正在使用人工智能聊天机器人来帮助完善文本、生成或编辑代码、整理其领域的文献等等。 来自巴西的 Rafael Bretas 在日本生活了十多年&#xff0c;日语说得很好。书面日语的各个方面&#xff0c;例如严格的礼貌等级制度&…

【Linux基础IO篇】用户缓冲区、文件系统、以及软硬链接

【Linux基础IO篇】用户缓冲区、文件系统、以及软硬链接 目录 【Linux基础IO篇】用户缓冲区、文件系统、以及软硬链接深入理解用户缓冲区缓冲区刷新问题缓冲区存在的意义 File模拟实现C语言中文件标准库 文件系统认识磁盘对目录的理解 软硬链接软硬链接的删除文件的三个时间 作者…

2023.11.11 hive中的内外部表的区别

一.内部表操作 ------------------------------1内部---------------------------- --建库 create database hive2; --用库 use hive2; --删表 drop table t1; --建表 create table if not exists t1(id int,name string,gender string ); --复制内部表 --复制表结构:CREATE T…

计算机视觉中目标检测的数据预处理

本文涵盖了在解决计算机视觉中的目标检测问题时&#xff0c;对图像数据执行的预处理步骤。 首先&#xff0c;让我们从计算机视觉中为目标检测选择正确的数据开始。在选择计算机视觉中的目标检测最佳图像时&#xff0c;您需要选择那些在训练强大且准确的模型方面提供最大价值的图…

基于ubuntu 22, jdk 8x64搭建图数据库环境 hugegraph--google镜像chatgpt

基于ubuntu 22, jdk 8x64搭建图数据库环境 hugegraph download 环境 uname -a #Linux whiltez 5.15.0-46-generic #49-Ubuntu SMP Thu Aug 4 18:03:25 UTC 2022 x86_64 x86_64 x86_64 GNU/Linuxwhich javac #/adoptopen-jdk8u332-b09/bin/javac which java #/adoptopen-jdk8u33…

2023年05月 Python(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 下列程序段的运行结果是?( ) def s(n):if n==0:return 1else:

畅通工程之局部最小花费问题 (C++)

目录 题目&#xff1a; 思路&#xff1a; 代码&#xff1a; 结果 题目&#xff1a; 思路&#xff1a; 详细思路都在代码注释里 。 代码&#xff1a; #include<iostream>//无向图邻接矩阵 #include<map> #include<algorithm> #define mvnum 1005 using …

​​​​​​​​​​​​​​汽车网络信息安全分析方法论

目录 1.典型信息安全分析方法 1.1 HEAVENS威胁分析模型 1.2 OCTAVE威胁分析方法 1.3 Attack Trees分析方法 2. 功能安全与信息安全的关系讨论 与Safety的典型分析方法一样&#xff0c;Security也有一些典型的信息安全威胁分析方法(TARA分析)&#xff0c;根据SAE J3061、I…

cortex-A7核 中断实验(按键中断实验)

1.选择按键触发方式 下降沿 2.解决消抖的方法 1&#xff09;ARM中&#xff1a;延时消抖 2&#xff09;linux驱动开发&#xff1a;定时器函数 3.框图 内部流程框图&#xff1a; 需要RCC GPIO EXTI GIC章节 中断触发流程&#xff1a; 4.RCC 章节 1&#xff09;使能GPIOF组 …

如何更好的使用Copilot

Copilot从诞生到现在过去了挺长时间了&#xff0c;大家对Copilot的评价算是褒贬不一吧。有些人觉得Copilot高效且神奇&#xff0c;可以对自己的工作大大提效&#xff1b;有些觉得也就那样&#xff0c;为什么要花那么多钱做这个事情&#xff0c;钱它不香吗&#xff1f; 从最开始…

nodejs+vue+python+PHP+微信小程序-安卓- 基于小程序的高校后勤管理系统-计算机毕业设计

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

SQL 聚合函数

前言 SQL中的聚合函数是对一组值执行计算&#xff0c;并返回单个值的函数。 常用的聚合函数有&#xff1a; 函数作用AVG&#xff08;&#xff09;求平均值MAX&#xff08;&#xff09;求最大值MIN&#xff08;&#xff09;求最小值SUM&#xff08;&#xff09;求和COUNT&…

Python开源项目RestoreFormer(++)——人脸重建(Face Restoration),模糊清晰、划痕修复及黑白上色的实践

有关 Python 和 Anaconda 及 RestoreFormer 运行环境的安装与设置请参阅&#xff1a; Python开源项目CodeFormer——人脸重建&#xff08;Face Restoration&#xff09;&#xff0c;模糊清晰、划痕修复及黑白上色的实践https://blog.csdn.net/beijinghorn/article/details/134…

25期代码随想录算法训练营第十四天 | 二叉树 | 递归遍历、迭代遍历

目录 递归遍历前序遍历中序遍历后序遍历 迭代遍历前序遍历中序遍历后序遍历 递归遍历 前序遍历 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # …