深入解析JVM:双亲委派机制的原理与实践

双亲委派机制

  • 引言
  • 概述
    • 流程
      • 工作原理:
    • 优势
    • 自定义类加载器
    • 实际应用

在这里插入图片描述

主页传送门:📀 传送

引言


  在Java虚拟机(JVM)中,类加载是一个重要的概念,而双亲委派机制是类加载的核心之一。本文将深入研究双亲委派机制,了解它是如何影响Java类加载的。

概述


  类加载是Java虚拟机的一种机制,用于将类的二进制数据读入内存并解析成Class对象。每个Java类都必须由某个类加载器加载到内存中。Java类加载器是通过双亲委派模型实现的,这种模型可以保证同一个类只会被同一个类加载器加载。

  JVM双亲委派机制是Java类加载器的一种工作机制,它的核心思想是:当一个类加载器收到类加载请求时,首先将这个请求委托给父类加载器去完成,依次递归下去,如果父类加载器可以完成该加载任务,就成功返回;只有父类加载器无法完成此加载请求时(找不到此类),子加载器才会尝试自己去加载。这种机制使得Java的核心API能确保在各种环境和应用中保持稳定。

流程


  当一个类加载器收到类加载请求时,会先判断该请求是否合法,如果不合法则直接拒绝;否则,会先将该请求委托给其父类加载器去完成。如果父类加载器无法完成该加载任务,则子类加载器才会尝试自己去加载。

具体来说,双亲委派流程如下:

1)当一个类加载器接收到类加载请求时,首先检查该请求是否合法。

2)如果该请求不合法,则直接拒绝。

3)如果该请求合法,则判断该请求是否需要使用引导类加载器。如果需要使用引导类加载器,则将该请求委托给引导类加载器去完成。

4)如果不需要使用引导类加载器,则将该请求委托给父类加载器去完成。如果父类加载器无法完成该加载任务,则子类加载器才会尝试自己去加载。

5)如果父类加载器可以完成该加载任务,则成功返回。

6)如果所有的父类加载器都无法完成该加载任务,则子类加载器会尝试自己去加载。

图示如下
在这里插入图片描述

工作原理:


  1. 层次结构

    • Java类加载器被组织成一个层次结构,通常包括三个层次:启动类加载器(Bootstrap Class Loader)、扩展类加载器(Extension Class Loader)和应用程序类加载器(Application Class Loader)。这些加载器之间存在父子关系,构成了一颗树状结构。
  2. 加载类请求

    • 当程序需要加载一个类时,当前类加载器首先会检查自己是否已经加载过这个类。如果已加载,它会返回该类的引用。

    • 如果当前类加载器没有加载过这个类,它会将加载请求委派给其父加载器。

  3. 委派给父加载器

    • 父加载器接收到委派请求后,会依照同样的方式检查是否已加载该类。如果已加载,它会返回该类的引用。

    • 如果父加载器也没有加载过该类,它会继续将加载请求委派给其自己的父加载器。这个过程会一直持续,直到达到启动类加载器为止。

  4. 启动类加载器

    • 启动类加载器是位于类加载器层次结构的根部,它通常由JVM的实现提供,负责加载Java核心类库(如java.lang包中的类)。

    • 如果启动类加载器也无法加载该类,它会抛出ClassNotFoundException,指示类找不到。

  5. 加载成功或失败

    • 如果某个父加载器成功加载了类,它将返回该类的引用给子加载器,并加载过程结束。

    • 如果所有父加载器都无法加载该类,当前类加载器会尝试自己加载类。如果加载成功,它会将类引用返回给请求者,加载过程结束。

    • 如果加载失败,当前类加载器会抛出ClassNotFoundException,指示类找不到。

  通过这种双亲委派模型,类加载器可以确保类的唯一性和安全性。核心类库由启动类加载器加载,避免了用户代码替换核心类的风险。同时,类加载器还可以自定义,以满足应用程序的需求,这有助于实现插件化和动态加载类的功能。双亲委派模型是Java类加载机制的一个关键概念,有助于保障Java程序的稳定性和安全性。

优势


  1. 类的唯一性和一致性(Class Uniqueness and Consistency)

    • 双亲委派模型确保了类的唯一性,因为一个类只会被加载一次。如果一个类已经被加载,即使在不同的类加载器命名空间中也不会再次加载,避免了类的多重定义问题。

    • 这有助于维持类加载的一致性,确保在不同的类加载器层次结构中使用的类是同一个版本,避免了类冲突。

  2. 安全性(Security)

    • 双亲委派模型有助于提高Java应用程序的安全性。核心类库由启动类加载器加载,这些类库的完整性受到更严格的控制。

    • 用户自定义类不容易替代核心类库,从而防止潜在的恶意代码注入或不安全的类加载。

  3. 避免重复加载(Avoidance of Redundant Loading)

    • 双亲委派模型避免了重复加载相同的类。当一个类被加载后,它会被缓存,之后的加载请求会直接返回缓存的类引用,提高了性能并节省了内存。
  4. 层次结构(Hierarchy)

    • 双亲委派模型建立了一个层次结构,包括启动类加载器、扩展类加载器和应用程序类加载器。这种层次结构使类加载器的管理更加清晰,有助于隔离不同的类。
  5. 动态加载和模块化(Dynamic Loading and Modularity)

    • 双亲委派模型允许开发人员创建自定义类加载器,这有助于实现动态加载类和模块化的应用程序设计。

    • 插件系统和动态模块化系统可以受益于这种灵活性,可以在运行时加载新的类和模块,而不需要重新启动应用程序。

自定义类加载器


  绕过Java的双亲委派机制通常不是一个推荐的做法,因为双亲委派机制是为了确保类加载的安全性和一致性而设计的。然而,在某些情况下,您可能需要绕过双亲委派机制,例如在实现自定义类加载器时,或者在特殊需求下执行一些高级操作。以下是一些可实现的方法:

  1. 自定义类加载器
// 自定义类加载器
class MyClassLoader extends ClassLoader {// 构造函数,需要指定父加载器public MyClassLoader(ClassLoader parent) {super(parent);}// 重写类加载方法@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException {// 在加载类之前可以进行一些自定义的操作System.out.println("Loading class: " + name);// 尝试使用父加载器加载类try {return super.loadClass(name);} catch (ClassNotFoundException e) {// 如果父加载器无法加载,自己尝试加载类byte[] classData = loadClassData(name);if (classData == null) {throw new ClassNotFoundException();} else {// 使用defineClass方法定义类return defineClass(name, classData, 0, classData.length);}}}// 模拟从文件或其他来源加载类字节码数据private byte[] loadClassData(String className) {// 此处可以根据类名加载对应的字节码数据// 这里简化为返回一个空的字节数组return null;}
}public class ClassLoadingExample {public static void main(String[] args) {// 创建一个自定义类加载器,以系统类加载器作为父加载器MyClassLoader myClassLoader = new MyClassLoader(ClassLoader.getSystemClassLoader());try {// 使用自定义加载器加载一个类Class<?> customClass = myClassLoader.loadClass("com.example.CustomClass");// 创建类的实例并调用方法Object instance = customClass.newInstance();customClass.getMethod("hello").invoke(instance);} catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {e.printStackTrace();}}
}

  在上述示例中,创建了一个自定义的类加载器MyClassLoader,它尝试加载名为"com.example.CustomClass"的类。如果父加载器无法加载,它会从模拟的数据源加载类字节码数据,并使用defineClass方法定义类。

  1. 使用Thread.currentThread().setContextClassLoader()

    • Java提供了Thread类的setContextClassLoader方法,可以设置线程上下文类加载器。这个类加载器将被用于在当前线程中加载类,而不受双亲委派机制的限制。
    ClassLoader customClassLoader = new MyClassLoader();
    Thread.currentThread().setContextClassLoader(customClassLoader);
    // 在这个线程中加载的类将使用自定义类加载器
    
  2. 使用Class.forName()的第二个参数

    • Class.forName()方法允许您通过指定类加载器来加载类,可以绕过双亲委派机制。
    ClassLoader customClassLoader = new MyClassLoader();
    Class<?> myClass = Class.forName("com.example.MyClass", true, customClassLoader);
    

注意:
   绕过双亲委派机制可能会引入类加载的不一致性和安全性问题,因此应慎重使用。通常情况下,最好遵循双亲委派模型,只在确实需要时才考虑绕过它,同时要确保谨慎处理自定义加载器和类加载操作,以防止潜在的问题。

实际应用


双亲委派机制在实际Java开发中有多种应用。其中一些常见的应用包括:

  1. 安全性和防止类冲突:通过双亲委派机制,Java确保核心类库只由启动类加载器加载,从而提高了系统的安全性。这样可以防止用户自定义的类意外地替代核心类库。

  2. 模块化和插件系统:双亲委派模型使得创建模块化和插件化系统更容易。每个模块或插件可以有自己的类加载器,以避免类名冲突,同时还能够访问系统类和其他模块的类。

  3. 动态加载和热部署:双亲委派模型使得动态加载和热部署变得更加可行。自定义类加载器可以加载新的类或更新现有的类,而不必重新启动整个应用程序。

假设我们有一个简单的插件系统,每个插件都有自己的类加载器,并且可以动态加载和卸载插件:

// 自定义插件类加载器
class PluginClassLoader extends ClassLoader {public PluginClassLoader(ClassLoader parent) {super(parent);}public Class<?> loadPluginClass(String className) throws ClassNotFoundException {return super.loadClass(className);}
}// 插件接口
interface Plugin {void run();
}// 插件示例
class SamplePlugin implements Plugin {@Overridepublic void run() {System.out.println("SamplePlugin is running.");}
}public class PluginSystem {public static void main(String[] args) {// 创建一个应用程序类加载器ClassLoader appClassLoader = PluginSystem.class.getClassLoader();// 创建插件类加载器PluginClassLoader pluginClassLoader = new PluginClassLoader(appClassLoader);try {// 使用插件类加载器加载插件类Class<?> pluginClass = pluginClassLoader.loadPluginClass("SamplePlugin");// 创建插件实例并运行Plugin plugin = (Plugin) pluginClass.newInstance();plugin.run();} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {e.printStackTrace();}}
}

  在这个示例中,我们创建了一个自定义的插件类加载器PluginClassLoader,它继承自应用程序类加载器,并可以加载插件类。这允许我们在不破坏应用程序类加载器的情况下加载插件。这是一个简单的示例,实际的插件系统会更加复杂,但它演示了双亲委派模型的应用,使我们可以动态加载并运行插件代码。

参考文献
虚拟机规范
深入理解Java虚拟机
openjdk https://github.com/openjdk/jdk/tree/master/src/hotspot/share
Tomcat 自定义类加载器

在这里插入图片描述

  如果喜欢的话,欢迎 🤞关注 👍点赞 💬评论 🤝收藏  🙌一起讨论你的支持就是我✍️创作的动力!					  💞💞💞

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

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

相关文章

写代码生成流程图

我们在写文档&#xff0c;博客的时候&#xff0c;一般都会使用markdown语法&#xff0c;最常见的就是一些github开源项目的README。有时候会去画一些流程图&#xff0c;例如使用process.on或者xmind等第三方网站&#xff0c;然后截图插入到文档中。 今天我们介绍一种使用代码直…

分布式搜索引擎Elasticsearch

一、Elasticsearch介绍 1.Elasticsearch产生背景 大数据量的检索NoSql: not only sql,泛指非关系型的数据库Nginx的7层负载均衡和4层负载均衡2.Elasticsearch是什么 一个基于Lucene的分布式搜索和分析引擎,一个开源的高扩展的分布式全文检索引擎 Elasticsearch使用Java开发…

一次ES检索的性能优化经验记录

优化功能: 统一检索能力&#xff0c;为各服务所调用。 该接口并发压力大&#xff0c;压测效果不理想。 初步2k线程两台压测机预发环境压测结果两pod下为400qps左右&#xff0c;单pod 平均qps200&#xff0c;响应时间在五分钟之后达到了峰值&#xff0c;平响达到几十秒开外。 压…

跨境电商引流之Reddit营销,入门保姆级攻略

在当今竞争激烈的在线市场中&#xff0c;企业不断寻求新的方法来加强其数字营销工作。Reddit 是最受欢迎的社交媒体平台之一&#xff0c;为企业提供了巨大的潜力&#xff0c;可以通过引人入胜且相关的内容来接触目标受众。然而&#xff0c;将 Reddit 用于营销目的需要仔细考虑某…

企业专线成本高?贝锐蒲公英轻松实现财务系统远程访问

在办公及信息系统领域&#xff0c;许多企业纷纷采用金蝶等财务管理软件来提升运营效率。以某食品制造企业为例&#xff0c;该企业总部位于广州&#xff0c;并拥有湖北仙桃工厂、广州从化工厂和湖南平江工厂三大生产基地。为提高管理效率&#xff0c;该企业在广州总部局域网内部…

游戏社区-搭建的目的和意义是什么

在游戏社区中&#xff0c;用户的活跃度指标是至关重要的&#xff0c;因此在必要时&#xff0c;我们会进行指标转化&#xff0c;以丰富的内容形式来促进用户的活跃度&#xff1b;作为一个垂直社区&#xff0c;我们可以通过聚合和培养一批游戏KOL&#xff0c;建立用户之间的紧密联…

成都瀚网科技:抖音上线地方方言自动翻译功能

为了让很多方言的地域历史、文化、习俗能够以短视频的形式生产、传播和保存&#xff0c;解决方言难以被更多用户阅读和理解的问题&#xff0c;平台正式上线推出当地方言自动翻译功能。创作者可以利用该功能&#xff0c;将多个方言视频“一键”转换为普通话字幕供大众观看。 具体…

leetcode 23. 合并 K 个升序链表

2023.9.25 本题要合并k个有序链表&#xff0c;最朴素的方法可以联想到之前做的合并两个有序链表。 用一个for循环遍历lists数组&#xff0c;利用合并两个有序链表的代码&#xff0c;不断合并lists中的链表&#xff0c;最后返回头节点即可。 代码如下&#xff1a; /*** Definit…

vue-cli创建项目、vue项目目录结(运行vue项目)、ES6导入导出语法、vue项目编写规范

vue-cli创建项目、vue项目目录结构、 ES6导入导出语法、vue项目编写规范 1 vue-cli创建项目 1.1 vue-cli 命令行创建项目 1.2 使用vue-cli-ui创建 2 vue项目目录结构 2.1 运行vue项目 2.2 vue项目的目录结构 3 es6导入导出语法 4 vue项目编写规范 4.1 修改项目 4.2 以后…

VC++判断程序是否已经运行;仅运行一次

VC判断程序是否已经运行&#xff1b;仅运行一次 BOOL CClientApp::InitInstance() {...//判断程序是否已经运行&#xff1b;仅运行一次CreateMutex(NULL,true,_T("xxxxx")); //xxxxx&#xff1a;为程序标识码if(GetLastError()ERROR_ALREADY_EXISTS) { AfxMess…

C#求100-999之间的水仙花数,你知道多少个?让我们一起来探索!

目录 背景: 扩展: 水仙花数例子: 效果展示:​ 总结: 背景: 水仙花数&#xff08;Narcissistic number&#xff09;也被称为超完全数字不变数&#xff08;pluperfect digital invariant, PPDI&#xff09;、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数&#xff08;Armstrong…

博主老程序员长期个人接单

主要技术栈 &#xff1a; 后端: .net winform webapi 前端&#xff1a;vue2 vue3 微信小程序 数据库&#xff1a; sqlserver mysql 小程序案例&#xff1a;快猪小寓微信小程序客户端 后台管理系统 联系微信 或 QQ 35568701

网络编程day04(网络属性函数、广播、组播、TCP并发)

今日任务 对于newfd的话&#xff0c;最好是另存然后传入给分支线程&#xff0c;避免父子线程操作同一个文件描述符 ------------在tcp多线程服务端---------- 如果使用全局变量&#xff0c;或者指针方式间接访问&#xff0c;会导致所有线程共用一份newfd和cin&#xff0c;那么…

冲刺十五届蓝桥杯P0001阶乘求和

文章目录 题目描述思路分析代码解析 题目描述 思路分析 阶乘是蓝桥杯中常考的知识。 首先我们需要知道 int 和long的最大值是多少。 我们可以知道19的阶乘就已经超过了long的最大值&#xff0c;所以让我们直接计算202320232023&#xff01;的阶乘是不现实的。 所以我们需要…

基于Linux socket聊天室-多线程服务器模型(01)

​前言 socket在实际系统程序开发当中&#xff0c;应用非常广泛&#xff0c;也非常重要。实际应用中服务器经常需要支持多个客户端连接&#xff0c;实现高并发服务器模型显得尤为重要。高并发服务器从简单的循环服务器模型处理少量网络并发请求&#xff0c;演进到解决C10K&…

什么是推挽电路?

推挽电路原理&#xff1a; 可以简单理解为推和拉&#xff1b; 此电路总共用到两个元器件&#xff0c;对应图中的Q1----NPN三极管&#xff0c;Q2----PNP三极管&#xff0c;两个电阻R1和R2起到限流的作用&#xff1b;两个三极管的中间对应信号的输出。 下面就举例说明是如何工作的…

基于JAVA,SpringBoot和Vue的前后端分离的求职招聘系统

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 这个系统的研发背景是…

时序预测 | MATLAB实现POA-CNN-BiLSTM鹈鹕算法优化卷积双向长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现POA-CNN-BiLSTM鹈鹕算法优化卷积双向长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-BiLSTM鹈鹕算法优化卷积双向长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-BiLSTM鹈鹕算…

使用SPY++查看窗口信息去排查客户端UI软件问题

目录 1、使用SPY查看窗口的信息 2、使用SPY查看某些软件UI窗口用什么UI组件实现的 2.1、查看海康视频监控客户端安装包程序 2.2、查看华为协同办公软件WeLink 2.3、查看字节协同办公软件飞书 2.4、查看最新版本的Chrome浏览器 2.5、查看小鱼易连视频会议客户端软件 2.6…