【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.mzph.cn/news/124205.shtml

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

相关文章

并行和并发有什么区别?

并行和并发 并行和并发最早其实描述的是 Java 并发编程里面的概念。他们强调的是 CPU 处理任务的能力。简单来说&#xff1a; 并发&#xff0c;就是同一个时刻&#xff0c;CPU 能够处理的任务数量&#xff0c;并且对于应用程序来说&#xff0c;不会出现卡顿现象。并行&#x…

【Linux】冯诺依曼体系结构以及初始操作系统

文章目录 冯诺依曼体系结构操作系统概念设计OS的目的定位如何理解管理 总结系统调用和库函数概念 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识…

HDFS 基本 shell 操作

HDFS 基本 shell 操作 1.1 创建目录1.2 上传指令1.3 创建空文件1.4 向分布式文件系统中的文件里追加内容1.5 查看指令1.6 下载指令1.7 合并下载1.8 移动hdfs中的文件1.9 复制hdfs中的文件到hdfs的另一个目录1.10 删除命令1.11 查看磁盘利用率和文件大小1.12 修改权限1.13 修改文…

专门解决数学问题的大模型

01 项目介绍 LLEMMA&#xff1a;一个专门解决数学问题的开源大语言模型&#xff0c;能力超过所有已知的开源模型 LLEMMA由多个大学和Eleuther AI公司共同研发&#xff0c;模型能够理解和生成数学表达式、解决数学问题&#xff0c;并与其他计算工具&#xff08;如Python解释器…

修改el-date-picker宽度

<div style"width: 100%"><el-date-pickerstyle"width:100%"v-model"value"type"datetimerange"start-placeholder"开始日期"end-placeholder"结束日期":default-time"[12:00:00]"value-forma…

pytorch 入门 (五)案例三:乳腺癌识别-VGG16实现

本文为&#x1f517;小白入门Pytorch内部限免文章 &#x1f368; 本文为&#x1f517;小白入门Pytorch中的学习记录博客&#x1f366; 参考文章&#xff1a;【小白入门Pytorch】乳腺癌识别&#x1f356; 原作者&#xff1a;K同学啊 在本案例中&#xff0c;我将带大家探索一下深…

Response Header中不暴露Server(IIS)版本、ASP.NET及相关版本等信息

ASP MVC开发的Web默认情况下会在请求的回应中暴露Server、X-AspNet-Version、X-AspNetMvc-Version、X-Powered-By等相关服务端信息&#xff0c;公开这些敏感信息会存在一定的安全风险。 X-SourceFiles标头用于被IIS / IIS Express中某些调试模块理解&#xff0c;它包含到磁盘上…

【Vue】初步认识<script setup>语法糖和组合式 API

▒ 目录 ▒ &#x1f6eb; 导读需求开发环境 1️⃣ &#x1f6eb; 导读 需求 最近写代码的时候&#xff0c;发现<script setup>这样的代码&#xff0c;没见过&#xff0c;好奇&#xff0c;想知道。 所以就有了这篇文章。 很多文章都说setup是vue3的特权。但是&#xff…

Vue图片路径问题(动态引入)

vue项目中我们经常会遇到动态路径的图片无法显示的问题&#xff0c;以下是静态路径和动态路径的常见使用方法。 1.静态路径 在日常的开发中&#xff0c;图片的静态路径通过相对路径和绝对路径的方式引入。 相对路径&#xff1a;以.开头的&#xff0c;例如./、../之类的。就是…

MySQL主从架构

1 主从架构解决了什么问题 随着业务的持续增长&#xff0c;单体数据库满足不了业务的需求&#xff0c;可能会出现负载过重&#xff0c;操作数据库速度变慢的情况。为了解决这个问题&#xff0c;数据库一般采用一主一从、一主多从的架构。 为了操作提高效率&#xff0c;减轻压…

sql在线练习

SQLBolt - 学习 SQL - SQL 简介https://sqlbolt.com/拿走不谢&#xff01;&#xff01;&#xff01; UIUC什么乱七八糟的啊

探讨下前端测试的常见场景

前端测试 场景 这边指的测试是指白盒测试&#xff0c;用代码来测试代码。 测试有利于提升代码质量。 代码功能和需求一致。根据需求&#xff0c;写测试。测试通过了&#xff0c;则表明需求实现了。保证代码重构后&#xff0c;未改坏以前的功能。代码重构后&#xff0c;能通过…

一文告诉你样机是什么,分享几个常用的样机模板

一个项目的诞生通常需要经历头脑构思、绘制设计和最终着陆。在这个过程中&#xff0c;样机制作往往是在着陆实践之前进行的。俗话说&#xff1a;“样机使用得好&#xff0c;草稿过早”。样机设计是产品或网站最终设计的生动、静态和视觉表现。它为用户提供了一种模拟现实的方式…

信息系统项目管理师教程 第四版【第7章-项目立项管理-思维导图】

信息系统项目管理师教程 第四版【第7章-项目立项管理-思维导图】 课本里章节里所有蓝色字体的思维导图

【uniapp】短信验证码输入框

需求是短信验证码需要格子输入框 如图 网上找了一个案例改吧改吧 直接上代码 结构 <template><view class"verify-code"><!-- 输入框 --><input id"input" :value"code" class"input" :focus"isFocus"…

数据结构之树(图解)

文章目录 前言一、树是什么&#xff1f;二、树的特点三、树的相关概念四、树的表示方法&#xff08;孩子兄弟表示法&#xff09;总结 前言 在学习完线性结构&#xff0c;例如顺序表、链表、栈、队列后&#xff0c;我们要开始学习一个新的数据结构----树 一、树是什么&#xf…

小白如何在一个月写一篇论文(中文核心,SCI)

小白如何半年发3篇sci的我教你如何快速“水”一篇sci论文_哔哩哔哩_bilibili 计算机视觉&#xff0c;cv领域 半年发3篇sci的我教你如何快速“水”一篇sci论文 计算机视觉(辅导 SCI EI 核心) 微信&#xff1a;whbwqq123或主页加up 小白如何快速写出一篇论文并成功发表&…

Elasticsearch:使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation (二)

这是继上一篇文章 “Elasticsearch&#xff1a;使用 Open AI 和 Langchain 的 RAG - Retrieval Augmented Generation &#xff08;一&#xff09;” 的续篇。在这篇文章中&#xff0c;我主要来讲述 ElasticVectorSearch 的使用。 我们的设置和之前的那篇文章是一样的&#xff…

【C】C语言文件操作

1.为什么使用文件 我们前面学习结构体时&#xff0c;写通讯录的程序&#xff0c;当通讯录运行起来的时候&#xff0c;可以给通讯录中增加、删除数据&#xff0c;此时数据是存放在内存中&#xff0c;当程序退出的时候&#xff0c;通讯录中的数据自然就不存在了&#xff0c;等下…

大彩串口屏读写文件问题

分区 本文使用的是大彩串口屏M系列的&#xff1a; 串口屏内部有三个分区&#xff0c;分别为A、B、C三个区&#xff1a; A区&#xff1a;系统区&#xff0c;存储组态工程文件 B区&#xff1a;数据区&#xff0c;存储配置信息&#xff0c;记录数据、历史曲线等 C区&#xff1a;备…