【JVM】类加载器

【JVM】类加载器

文章目录

  • 【JVM】类加载器
    • 0. 类加载器概述
    • 1. 类加载器的分类
      • 1.1 启动类加载器
      • 1.2 Java中的默认类加载器
        • 1.2.1 扩展类加载器
        • 1.2.2 应用程序类加载器
    • 2. 双亲委派机制
      • 2.1 类的双亲委派机制是什么?
      • 2.2 打破双亲委派机制
        • 2.2.1 自定义类加载器
        • 2.2.2 线程上下文类加载器
      • 2.3 OSGi模块化
    • 3. 总结

0. 类加载器概述

类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。
image-20231025141442943
image-20231025141442943


1. 类加载器的分类

类加载器分为两类:

  1. Java代码中实现的类加载器
  2. JVM底层源码实现的类加载器

image-20231025141442943

jdk8和8之后版本的类加载器的设计差别较大,jdk8及之前的版本中默认的类加载器有如下几种:

  1. JVM底层实现(C++):
    • 启动类加载器Bootstrap:加载Java中最核心的类
  2. Java:
    • 扩展类加载器Extension:允许扩展Java中比较通用的类
    • 应用程序类加载器Application:加载应用使用的类

1.1 启动类加载器

**启动类加载器(Bootstrap ClassLoader)**是由 Hotspot 虚拟机提供的,使用C++编写的类加载器。默认加载Java安装目录/jre/lib下的类文件,比如 rt.jar,tools.jar,resources.jar等。

使用启动类加载器去加载用户jar包有两种方式:

  1. 将jar包放入 jre/lib 目录下进行扩展
    • 不推荐,尽可能不要去更改JDK安装目录中的内容,因为就算将jar包放入该目录下也可能由于文件名不匹配的问题导致jar包不会正常的被加载。
  2. 使用参数进行扩展
    • 推荐,使用 -Xbootclasspath/a:路径/jar包名.jar 进行扩展。

1.2 Java中的默认类加载器

扩展类加载器应用程序类加载器都说JDK中提供的、使用Java编写的类加载器。它们的源码都位于 sun.misc.Launcher 中,是一个静态内部类。继承自 URLClassLoader可以通过目录或者指定jar包将字节码文件加载到内存中

image-20231025164359439


1.2.1 扩展类加载器

**扩展类加载器(Extension ClassLoader)**是jdk中提供的、使用Java编写的类加载器。默认加载Java安装目录 /jre/lib/ext 下的类文件。

通过扩展类加载器去加载用户jar包的方式:

  1. 放入 jre/lib/ext 下进行扩展。
    • 不推荐,尽可能不要去更改jdk安装目录中的内容。
  2. 使用参数进行扩展
    • 推荐,使用 -Djava.ext.dirs=jar包目录 进行扩展,这种方式会覆盖掉原始目录,随意我们应该用 ;(windows)或 :(macos/linux)追加上原始目录。

1.2.2 应用程序类加载器

应用程序类加载器(AppClassLoader):面向我们用户的加载器,负责加载当前应用 classpath 下的所有 jar 包和类。


2. 双亲委派机制

由于JVM中有多个类加载器,双亲委派机制的核心是解决一个类到底由谁加载的问题。

双亲委派机制的作用:

  1. 保证类加载的安全性:通过双亲委派机制避免恶意代码替换jdk中的核心类库,比如 java.lang.String ,确保核心类库的完整性和安全性。
  2. 避免重复加载:双亲委派机制可以避免同一个类被多次加载。

双亲委派机制指的是:当一个类加载器接收到加载类的任务时,会自底向上查找是否加载过,再由顶向下进行加载
image-20231025141442943

  1. 向上查找:
    • 向上查找如果已经加载过,就直接返回Class对象,加载过程结束。这样就能避免一个类重复加载
  2. 向下加载:
    • 如果所有父类加载器都无法加载该类,则由当前类加载器自己尝试加载。所以看上去是自顶向下尝试。
    • 第二次再去加载相同的类,仍会向上进行委派,如果某个类加载器加载过就会直接返回。

每个Java实现的类加载器中都保存了一个成员变量名为 parent 的类加载器,**可以理解为它的上级,并不是继承关系。**应用程序类加载器的parent父类加载器是扩展类加载器,而扩展类加载器的parent是空,因为启动类加载器由C++实现,无法在Java中获得。


2.1 类的双亲委派机制是什么?

类的双亲委派机制是什么?

  1. 当一个类加载器去加载某个类的时候,会自底向上查找是否加载过,如果加载过就直接返回Class对象,如果一直到最顶层的类加载器都没有加载,再自顶向下进行加载。
  2. 应用程序类加载器的父类加载器是扩展类加载器,扩展类加载器的父类加载器是启动类加载器。
  3. 双亲委派机制的好处:
    • 避免恶意代码替换jdk中核心类库,确保核心类库的完整性和安全性。
    • 避免类被重复加载。

2.2 打破双亲委派机制

打破双亲委派机制的三种方式:

  1. 自定义类加载器:自定义类加载器并且重写 loadClass 方法,就可以将双亲委派机制的代码去除。
  2. 线程上下文类加载器:利用上下文类加载器加载类,比如JDBC和JNDI等。
  3. Osgi框架的类加载器:历史上Osgi框架实现了一套新的类加载器机制,允许同级之间委托进行类的加载。

2.2.1 自定义类加载器
  • 一个Tomcat程序中可以运行多个Web应用,如果这两个应用中出现了相同限定名的类,比如Servlet类,Tomcat要保证这两个类都能加载并且它们应该是不同的类。
  • 如果不打破双亲委派机制,当应用类加载器加载Web应用1中的MyServlet之后,Web应用2中相同限定名的MyServlet类就无法被加载了。
    image-20231025141442943

Tomcat使用了自定义类加载器来实现应用之间类的隔离。每一个应用会有一个独立的类加载器加在对应的类。
image-20231025141442943

ClassLoader中包含了4个核心方法(双亲委派机制的核心代码就位于loadClass方法中):
image-20231025141442943

打破双亲委派机制的关键就是重写 loadClass 方法中的逻辑。


2.2.2 线程上下文类加载器

JDBC中使用了 DriverManager 来管理项目中引入的不同数据库的驱动,比如mysql驱动,oracle驱动。
image-20231025141442943

DriverManager 类位于 rt.jar 包中,由启动类加载器加载。而依赖中的mysql驱动对应的类,由应用程序类加载器来加载。这就违反了双亲委派机制。
image-20231025141442943

DriverManager 使用SPI机制,最终加载jar包中对应的驱动类。
image-20231025141442943

那么SPI机制是如何获取到应用程序类加载器的呢?

SPI中使用了线程上下文中保存的类加载器进行类的加载,这个类加载器一般是应用程序类加载器。

public static <S> ServiceLoader<S> load(Class<S> service){ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service,cl);
}

完整流程:

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

思考

JDBC案例真的打破了双亲委派机制吗?

有两种说法:

  1. 打破了双亲委派机制:这种由启动类加载器加载的类,委派应用程序类加载器去加载类的方式,打破了双亲委派机制。
  2. 没有打破双亲委派机制:类加载流程中,没有违反双亲委派机制。因为 DriverManager 位于rt.jar包下,由启动类加载器加载,而mysql驱动位于classpath,由应用程序类加载器加载,没有问题。

2.3 OSGi模块化

历史上,OSGi模块化框架。它存在同级之间的类加载器的委托加载。OSGi还使用类加载器实现了热部署(在服务不停止的情况下,动态更新字节码文件到内存中)的功能。

image-20231025224629529


3. 总结

  1. 类加载器的作用是什么?

答:类加载器(ClassLoader)负责在类加载过程当中获取字节码并加载到内存中转换成byte[],接下来调用虚拟机底层方法将byte[]转换成方法区和堆中的数据。


  1. 有几种常见的类加载器?

答:

  • 启动类加载器:加载核心类
  • 扩展类加载器:加载扩展类
  • 应用程序类加载器:加载应用classpath中的类
  • 自定义类加载器:重写findClass方法

  1. 什么是双亲委派机制

答:每个Java实现的类加载器中都保存了一个成员变量叫 parent 的类加载器。自底向上查找是否加载过,再由顶向下进行加载。避免核心类被应用程序重写并覆盖的问题,提升了安全性。
image-20231025141442943


  1. 怎么打破双亲委派机制?

答:

  1. 重写loadClass方法。
  2. JNDI、JDBC、JCE、JAXB和JBI等框架使用了SPI机制+线程上下文类加载器
  3. OSGi实现了一套类加载机制,允许同级类加载器之间互相调用。

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

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

相关文章

Android多张图片rotation旋转角度叠加/重叠堆放

Android多张图片rotation旋转角度叠加/重叠堆放 <?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:app"http://schemas.android.com/apk/res-auto"…

vue中electron与vue通信(fs.existsSync is not a function解决方案)

electron向vue发送消息 dist/main.js (整个文件配置在另一条博客里) win new BrowserWindow({width:1920,height:1080,webPreferences: {// 是否启用Node integrationnodeIntegration: true, // Electron 5.0.0 版本之后它将被默认false// 是否在独立 JavaScript 环境中运行…

【网络协议】聊聊套接字socket

网络编程我们知道是通过socket进行编程的&#xff0c;其实socket也是基于TCP和UDP协议进行编程的。但是在socket层面是感知不到下层的&#xff0c;所以在设置参数的时候&#xff0c;其实是端到端协议智商的网络层和传输层。TCP是数据流所以设置为SOCK_STREAM&#xff0c;而UDP是…

【Linux】——使用yum进行软件安装和卸载Win和Linux文件交互

个人主页点击直达&#xff1a;小白不是程序媛 Linux系列专栏&#xff1a;Linux被操作记 目录 前言&#xff1a; Linux软件包管理器yum 什么是软件包 ​编辑软件查找 如何安装软件 如何卸载软件 lrzsz的使用 将Windows的文件传送到Linux 将Linux的文件传送到Windows …

C++ 自引用指针this(整理)

使用例子&#xff1a; #include <iostream> #include <Windows.h> using namespace std; class A { public:A(int x1){x x1;}void disp(){cout<<"this"<<this<<" when x"<<this->x<<endl;} private:int x;…

Go学习第十四章——Gin请求与响应

Go web框架——Gin请求与响应 1 响应1.1 String1.2 JSON&#xff08;*&#xff09;1.3 HTML&#xff08;*&#xff09;1.4 XML1.5 文件&#xff08;*&#xff09; 2 请求2.1 请求参数查询参数 (Query)动态参数 (Param)表单参数 (PostForm)原始参数 (GetRawData) 2.2 请求头2.3 …

【2023.10.30练习】C语言-循环右移字符

计算机能力挑战初赛2020.19题 题目描述&#xff1a; 现要对一个由字符a-z和A-Z组成的字符串进行解密&#xff0c;已知加密规则是&#xff1a; 字符串中所有字符分别在大写或小写的字母表中被循环左移5位(fGh-->aBc)&#xff0c; 输入&#xff1a;一个加密过的字符串&#…

GO学习之 通道(nil Channel妙用)

GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 8、GO学习之 函数(Function) 9、GO学习之 接口(Interface) 10、GO学习之 网络通信(Net/Htt…

【unity3D】Rect Transform组件

&#x1f497; 未来的游戏开发程序媛&#xff0c;现在的努力学习菜鸡 &#x1f4a6;本专栏是我关于游戏开发的学习笔记 &#x1f236;本篇是unity的Rect Transform组件 Rect Transform组件 基础知识详细介绍补充 基础知识 Rect Transform是Unity中的一个UI组件&#xff0c;用于…

RabbitMQ消费者的可靠性

目录 一、消费者确认 二、失败重试机制 2.1、失败处理策略 三、业务幂等性 3.1、唯一消息ID 3.2、业务判断 3.3、兜底方案 一、消费者确认 RabbitMQ提供了消费者确认机制&#xff08;Consumer Acknowledgement&#xff09;。即&#xff1a;当消费者处理消息结束后&#x…

synchronized 同步锁的思考

经过前面的分析&#xff0c;我们大概对同步锁有了一些基本的认识&#xff0c;同步锁的本质就是实现多线程的互斥&#xff0c;保证同一时刻只有一个线程能够访问加了同步锁的代码&#xff0c;使得线程安全性得到保证。下面我们思考一下&#xff0c;为了达到这个目的&#xff0c;…

Linux 块设备驱动实验

前面我们都是在学习字符设备驱动&#xff0c;本章我们来学习一下块设备驱动框架&#xff0c;块设备驱动是 Linux 三大驱动类型之一。块设备驱动要远比字符设备驱动复杂得多&#xff0c;不同类型的存储设备又 对应不同的驱动子系统&#xff0c;本章我们重点学习一下块设备相关驱…

图像分类任务ViT与CNN谁更胜一筹?DeepMind用实验证明

精华置顶 墙裂推荐&#xff01;小白如何1个月系统学习CV核心知识&#xff1a;链接 点击CV计算机视觉&#xff0c;关注更多CV干货 今天跟大家分享DeepMind发表的一篇技术报告&#xff0c;通过实验得出&#xff0c;CNN与ViT的架构之间虽然存在差异&#xff0c;但同等计算资源的预…

人工智能AI 全栈体系(九)

第一章 神经网络是如何实现的 如何用神经网络处理不等长文本的方法&#xff1f; 八、循环神经网络&#xff08;RNN: Recurrent Neural Network&#xff09; 处理不等长文本的神经网络 – 循环神经网络 RNN。 1. 从句子理解说起 上次讲了用词向量表示词&#xff0c;一句话也…

北邮22级信通院数电:Verilog-FPGA(7)第七周实验(2):BCD七段显示译码器(关注我的uu们加群咯~)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 关注作者的uu们可以进群啦~ 目录 一.verilog代码…

Kubernetes Label Selector

Author&#xff1a;rab 目录 前言一、Labels1.1 定义1.2 案例1.2.1 节点标签1.2.2 对象标签 二、Selector2.1 Node Selector2.2 Service Selector2.3 Deployment Selector2.4 StatefulSet Selector2.5 DaemonSet Selector2.6 HorizontalPodAutoscaler Selector2.7 NetworkPolic…

【C】柔性数组

柔性数组 也许你从来没有听说过柔性数组&#xff08;flexible array)这个概念&#xff0c;但是它确实是存在的。 C99 中&#xff0c;结构中的最后一个元素允许是未知大小的数组&#xff0c;这就叫做『柔性数组』成员。 例如&#xff1a; 柔性数组的特点 结构中的柔性数组成员前…

ZKP7.1 Polynomial Commitments Based on Error-correcting Codes (Background)

ZKP学习笔记 ZK-Learning MOOC课程笔记 Lecture 7: Polynomial Commitments Based on Error-correcting Codes (Yupeng Zhang) Recall: common paradigm for efficient SNARK A polynomial commitment scheme A polynomial interactive oracle proof (IOP) SNARK for gene…

CAN总线通信协议

Reference video: 趋近于完美的通讯 CAN总线&#xff01;4分钟看懂&#xff01; CAN通信精华整理&#xff0c;汽车工程师必备技能&#xff0c;一个视频带你轻松掌握&#xff01; 写在前面&#xff1a;CAN通信就三个要点 - 波特率的配置 - 过滤寄存器的配置与理解&#xff08;…

数组与链表算法-单向链表算法

目录 数组与链表算法-单向链表算法 C代码 单向链表插入节点的算法 C代码 单向链表删除节点的算法 C代码 对单向链表进行反转的算法 C代码 单向链表串接的算法 C代码 数组与链表算法-单向链表算法 在C中&#xff0c;若以动态分配产生链表节点的方式&#xff0c;则可以…