当前位置: 首页 > news >正文

JVM类加载和双亲委派机制

当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把类加载到JVM,本文主要说明类加载机制和其具体实现双亲委派模式。

一、类加载机制

类加载过程

类加载的过程是将类的字节码加载到内存中的过程,主要包括:加载-->链接-->初始化,其中链接还包括验证、准备、解析3个步骤。

  1. 加载:将class文件加载到内存,在方法区生成运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应Class实例的引用,生成的Class实例对象放到堆中;
  2. 验证:验证这个class文件是否合法,包括文件格式的校验,元数据类型的校验等;
  3. 准备:为类变量分配内存空间,但此时只是初始化为默认值而非真实值,但对于final变量此时会初始化为真实值;
  4. 解析:将符号引用(相对引用)转换为直接引用,符号引用是class文件的相对表达方式,直接引用就是在该系统里地址指针,比如hello()方法为符号引用,0x12345678为直接引用;
  5. 初始化:初始化类变量成真实值,初始化静态代码段,实际执行的是类构造器 方法。

image-20230909143853603

类加载过程是懒加载的策略,只有当该类被使用了才会被初始化,实际就是调用classLoader的 方法执行的过程;会触发类的初始化操作条件为:(1)需要创建新的对象,执行了new操作;(2)调用了类的静态变量或静态方法;(3)通过反射机制来获取某个类的时候;

利用new实例化对象的过程

Java的对象实例化的过程是调用 方法,在进行new操作的时候会执行实例化操作,实例化的过程主要 是调用构造方法的过程。在进行对象实例化前,已经初始化静态变量和静态代码段,实例化过程会初始化变量和代码块、调用构造方法进行实例化,利用new操作实例化对象的过程:

二、双亲委派

类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器:

  • 引导类加载器Bootstrap ClassLoader:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等;
  • 扩展类加载器Extension ClassLoader:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包;
  • 应用程序类加载器Application ClassLoader:负责加载ClassPath路径下的类包,主要就是加载你自己写的那些类;
  • 自定义加载器Customer ClassLoader:负责加载用户自定义路径下的类包;
image-20230909144438391

以下代码打印出各个类关联的ClassLoader情况:

public class TestJDKClassLoader {public static void main(String[] args) {System.out.println(String.class.getClassLoader());System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());System.out.println(TestJDKClassLoader.class.getClassLoader().getClass().getName());System.out.println();ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();ClassLoader extClassloader = appClassLoader.getParent();ClassLoader bootstrapLoader = extClassloader.getParent();System.out.println("the bootstrapLoader : " + bootstrapLoader);System.out.println("the extClassloader : " + extClassloader);System.out.println("the appClassLoader : " + appClassLoader);System.out.println();System.out.println("bootstrapLoader加载以下文件:");URL[] urls = Launcher.getBootstrapClassPath().getURLs();for (int i = 0; i < urls.length; i++) {System.out.println(urls[i]);}System.out.println();System.out.println("extClassloader加载以下文件:");System.out.println(System.getProperty("java.ext.dirs"));System.out.println();System.out.println("appClassLoader加载以下文件:");System.out.println(System.getProperty("java.class.path"));}
}

类加载的具体实现是通过双亲委派机制,加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。

简单理解就是:能父加载器做的事就父加载器做,父加载器做不了的事情才自己来做

设计双亲委派机制的好处是:

  1. 保障类的唯一性:ClassLoader的双亲委派模型保障一个类在类加载器的唯一性,父类已经加载了该类,子类就不再加载,保障被加载类的唯一性。
  2. 实现沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改。

类加载器实现原理

先说明类加载器本身初始化的逻辑:

  1. 在sun.misc.Launcher类中创建JVM启动器实例。
  2. 在Launcher构造方法内部,其创建了两个类加载器,分别是sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应用类加载器)。
  3. JVM默认使用Launcher的getClassLoader()方法返回的类加载器AppClassLoader的实例加载我们的应用程序。
//Launcher的构造方法public Launcher() {Launcher.ExtClassLoader var1;try {// 构造扩展类加载器,在构造的过程中将其父加载器设置为nullvar1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {//构造应用类加载器,在构造的过程中将其父加载器设置为ExtClassLoader,//Launcher的loader属性值是AppClassLoader,我们一般都是用这个类加载器来加载我们自己写的应用程序this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}//设置当前线程的类加载器为应用类加载器Thread.currentThread().setContextClassLoader(this.loader);String var2 = System.getProperty("java.security.manager");//...
}

再说明一下AppClassLoader的loadClass方法,该方法是进行class文件加载的方法,但最终会调用其父类ClassLoader的loadClass方法,该方法的大体逻辑如下:

  1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
  2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。
  3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
//ClassLoader的loadClass方法,里面实现了双亲委派机制
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// 检查当前类加载器是否已经加载了该类Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {  //如果当前加载器父加载器不为空则委托父加载器加载该类c = parent.loadClass(name, false);} else {  //如果当前加载器父加载器为空则委托引导类加载器加载该类c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();//都会调用URLClassLoader的findClass方法在加载器的类路径里查找并加载该类c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {  //不会执行resolveClass(c);}return c;}
}

用户可以设置自定义类加载器来打破默认的双亲委派模式,主要是实现loadClass()方法,在这个方法中可以自定义加载class文件的逻辑。

public class MyClassLoaderTest {static class MyClassLoader extends ClassLoader {private String classPath;public MyClassLoader(String classPath) {this.classPath = classPath;}private byte[] loadByte(String name) throws Exception {name = name.replaceAll("\\.", "/");FileInputStream fis = new FileInputStream(classPath + "/" + name+ ".class");int len = fis.available();byte[] data = new byte[len];fis.read(data);fis.close();return data;}protected Class<?> findClass(String name) throws ClassNotFoundException {try {byte[] data = loadByte(name);return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}/*** 重写类加载方法,实现自己的加载逻辑,不委派给双亲加载* @param name* @param resolve* @return* @throws ClassNotFoundException*/protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}if (resolve) {resolveClass(c);}return c;}}}public static void main(String args[]) throws Exception {MyClassLoader classLoader = new MyClassLoader("xxx");//尝试用自己改写类加载机制去加载自己写的java.lang.String.classClass clazz = classLoader.loadClass("java.lang.String");Object obj = clazz.newInstance();Method method= clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}

打破双亲委托的方法:

通过线程上下文类加载器Thread Context ClassLoader#setContextClassLoader(ClassLoader c)可以指定当前线程的类加载器,如果创建线程时还未设置,它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。

本文由博客一文多发平台 OpenWrite 发布!

相关文章:

JVM类加载和双亲委派机制

当我们用java命令运行某个类的main函数启动程序时&#xff0c;首先需要通过类加载器把类加载到JVM&#xff0c;本文主要说明类加载机制和其具体实现双亲委派模式。 一、类加载机制 类加载过程&#xff1a; 类加载的过程是将类的字节码加载到内存中的过程&#xff0c;主要包括…...

P-MVSNet ICCV-2019 学习笔记总结 译文 深度学习三维重建

文章目录 5 P-MVSNet ICCV-20195.0 主要特点5.1 文章概述5.2 研究方法5.2.1 特征提取5.2.2 学习局域匹配置信5.2.3 深度图预测5.2.4 Loss方程MVSNet系列最新顶刊 对比总结5 P-MVSNet ICCV-2019 深度学习三维重建 P-MVSNet-ICCV-2019(原文、译文、批注) 下载 5.0 主要特点 …...

vueshowpdf 移动端pdf文件预览

1、安装 npm install vueshowpdf -S2、参数 属性说明类型默认值v-model是否显示pdf--pdfurlpdf的文件地址String- scale 默认放大倍数 Number1.2 minscale 最小放大倍数 Number0.8 maxscale 最大放大倍数 Number2 3、事件 名称说明回调参数closepdf pdf关闭事件-pdferr文…...

C#根据excel文件中的表头创建数据库表

C#根据excel文件中的表头创建数据库表 private void button1_Click(object sender, EventArgs e){string tableName tableNameTextBox.Text;string connectionString "";using (OpenFileDialog openFileDialog new OpenFileDialog()){openFileDialog.Filter &quo…...

js通过xpath定位元素并且操作元素以下拉框select为例

js也可以使用xpath定位元素&#xff0c;现在实例讲解。 页面上有一个下拉框&#xff0c;里面内容有三个&#xff0c;用F12看一下 一、使用xpath定位这个下拉框select eldocument.evaluate(//select[name"shoppingPreference"], document).iterateNext()二、为下拉框…...

数据类型

目录 1.数值类型 整数类型 int 小数类型 double 2.字符类型 固定长度字符串 char 可变长度字符串 varchar 3.日期时间类型 日期类型&#xff1a;date 日期时间类型&#xff1a;datetime MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article…...

vue 模板应用

一&#xff0c;模板应用也就是对DOM的操作 二&#xff0c;如何使用 通过标签里面添加ref 和vue中使用 this.$refs.ref的名字.操作 进行使用 <template><h3>模板引用</h3><div ref"cont" class"cont">{{ content }}</div>&…...

Golang教程与Gin教程合集,入门到实战

GolangGin框架GormRbac微服务仿小米商城项目实战视频教程Docker Swarm K8s云原生分布式部署 介绍&#xff1a; Go即Golang&#xff0c;是Google公司2009年11月正式对外公开的一门编程语言&#xff0c;它不仅拥有静态编译语言的安全和高性能&#xff0c;而 且又达到了动态语言开…...

国家网络安全周 | 天空卫士荣获“2023网络安全优秀创新成果大赛优胜奖”

9月11日上午&#xff0c;四川省2023年国家网络安全宣传周在泸州开幕。在开幕式上&#xff0c;为2023年网络安全优秀创新成果大赛——成都分站赛暨四川省“熊猫杯”网络安全优秀作品大赛中获奖企业颁奖&#xff0c;天空卫士银行数据安全方案获得优秀解决方案奖。 本次比赛由四川…...

Swift学习笔记一(Array篇)

目录 0 绪论 1 数组的创建和初始化 2.数组遍历 2.1通过键值对遍历 2.2 通过forEach遍历 2.3 通过for in遍历 2.3.1 for in 搭配 enumerated 2.3.2 for in的另一种形式 2.3.2 for in 搭配 indices 2.4 通过Iterator遍历器遍历 3 数组的操作 3.1 contains 判断数组包含…...

C++项目实战——基于多设计模式下的同步异步日志系统-②-前置知识补充-不定参函数

文章目录 专栏导读不定参函数C风格不定参函数不定参宏函数 专栏导读 &#x1f338;作者简介&#xff1a;花想云 &#xff0c;在读本科生一枚&#xff0c;C/C领域新星创作者&#xff0c;新星计划导师&#xff0c;阿里云专家博主&#xff0c;CSDN内容合伙人…致力于 C/C、Linux 学…...

C++使用Boost库加入UDP组播时程序崩溃

程序崩溃情况 本程序运行在Oracle VM VirtualBox虚拟的Ubuntu20.04上 terminate called after throwing an instance of ‘boost::wrapexceptboost::system::system_error’ what(): set_option: No such device 已放弃 (核心已转储) ** C使用Boost库加入组播的代码 #inclu…...

华为HCIA(四)

链路聚合可以负载分担&#xff0c;增加带宽&#xff0c;提高可靠性 Eth-trunk的传输速率和成员端口数量喝带宽有关 路由器分割广播域&#xff0c;交换机分割冲突域 指定端口&#xff1a;DP;根端口&#xff1a;RP;阻塞端口&#xff1a;AP 如果目的MAC不在交换机MAC中&…...

Qt --- Day01

效果图&#xff1a; 头像的圆形未实现 单击登陆&#xff0c;触发信号与槽 enter_widget.h #ifndef ENTER_H #define ENTER_H#include <QDialog> #include<QLabel> #include<QTimer> class enter_widget : public QDialog {Q_OBJECT public:explicit enter_…...

24.98万起,新一代AITO问界M7值得买吗?

监制 | 何玺 排版 | 叶媛 问界汽车新品来袭。 9月12日下午&#xff0c;问界汽车为全新的M7系列车型举行了发布会。华为常务董事余承东&#xff0c;在全网一片“遥遥领先”呼声的烘托下&#xff0c;上台发表演讲&#xff0c;详细介绍了M7的全面升级和各大亮点。 01 新一代AI…...

Java毕业设计 SSM SpringBoot 水果蔬菜商城

Java毕业设计 SSM SpringBoot 水果蔬菜商城 SSM 水果蔬菜商城 功能介绍 首页 图片轮播 关键字搜索商品 分类菜单 折扣大促销商品 热门商品 商品详情 商品评价 收藏 加入购物车 公告 留言 登录 注册 我的购物车 结算 个人中心 我的订单 商品收藏 修改密码 后台管理 登录 商品…...

前端JS中的异步编程与Promise

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 一、JavaScript的异步编步机制 二、事件循环&#xff08;Event Loop&#xff09;和任务队列&#xff08;Task Queue…...

Pytorch Advanced(二) Variational Auto-Encoder

自编码说白了就是一个特征提取器&#xff0c;也可以看作是一个降维器。下面找了一张很丑的图来说明自编码的过程。 自编码分为压缩和解码两个过程。从图中可以看出来&#xff0c;压缩过程就是将一组数据特征进行提取&#xff0c; 得到更深层次的特征。解码的过程就是利用之前的…...

Flask 使用 JWT(三)flask-jwt-extended

如果想要在 flask 中使用 JWT ,推荐使用 flask-jwt-extended 插件。 使用 pip 安装这个扩展插件的最简单方法是: pip install flask-jwt-extended基本使用 在接下来的案例中,我们看一下基本使用。我们可以使用 create_access_token() 函数用来生成实际的 JWT token。@jwt_r…...

堆与栈的区别

OVERVIEW 栈与堆的区别一、程序内存分区中的堆与栈1.栈2.堆3.堆&栈 二、数据结构中的堆与栈1.栈2.堆 三、堆的深入1.堆插入2.堆删除&#xff1a;3.堆建立&#xff1a;4.堆排序&#xff1a;5.堆实现优先队列&#xff1a;6.堆与栈的相关练习 栈与堆的区别 自整理&#xff0c;…...

5个简单步骤:在Windows电脑上直接安装Android应用的终极指南

5个简单步骤&#xff1a;在Windows电脑上直接安装Android应用的终极指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 你是否厌倦了在Windows电脑上使用Android模拟器…...

收藏!普通人零基础转行AI,3-5个月实现高薪就业的进阶指南

本文指出AI行业对非计算机专业人才的需求激增&#xff0c;半路转行者因具备行业经验而更具竞争力。文章澄清了转行AI的常见误区&#xff0c;强调“技术懂业务”是关键&#xff0c;并提供了普通人转行AI的3步走策略&#xff1a;选择AI算法、自然语言或应用工程师等低门槛岗位&am…...

从数据焦虑到数字资产:WeChatExporter如何重塑你的微信记忆管理

从数据焦虑到数字资产&#xff1a;WeChatExporter如何重塑你的微信记忆管理 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否曾因手机存储空间不足而不得不删除珍贵…...

Shell脚本错误处理实战:用sh-guard提升Bash脚本健壮性

1. 项目概述&#xff1a;一个为Shell脚本穿上“防护服”的守护者在Linux运维、自动化部署乃至日常的系统管理工作中&#xff0c;Shell脚本是我们最得力的助手。从简单的日志清理到复杂的CI/CD流水线&#xff0c;Shell脚本无处不在。然而&#xff0c;脚本的健壮性却常常被忽视。…...

WARPED框架:单目RGB驱动的机器人视觉运动策略学习

1. WARPED框架&#xff1a;单目RGB驱动的机器人视觉运动策略学习新范式在机器人模仿学习领域&#xff0c;如何高效获取高质量的示范数据一直是个核心挑战。传统方法通常需要昂贵的多视角相机阵列、深度传感器或专用硬件设备&#xff0c;这不仅增加了部署成本&#xff0c;更限制…...

如何高效获取网盘直链:8大平台的完整解决方案

如何高效获取网盘直链&#xff1a;8大平台的完整解决方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅…...

modbus 512 断线重连 db browser for sqlite

断线重连 private async Task HeartbeatLoopAsync(CancellationToken token) {// 监工一直循环干活&#xff0c;直到工长喊停工&#xff08;token.IsCancellationRequested&#xff09;while (!token.IsCancellationRequested){try{// 每隔一段时间检查一次&#xff08;最少20…...

KG与LLM:大模型时代的智能规划

这些文章给出的“推荐思路”可以浓缩成一句话 先用 Planner 产出 subgoal dependency acceptance criteria。再让 Router 判断每个子任务该走 向量RAG、KG、数据库还是工具。对需要关系、多跳、时序、因果的问题&#xff0c;用 KG / event graph 做结构化检索&#xff0c;而…...

新手工程师别慌!从零开始搞定一颗新Sensor的完整调试手册(附常见问题排查清单)

新手工程师别慌&#xff01;从零开始搞定一颗新Sensor的完整调试手册 刚拿到一颗新Sensor时&#xff0c;面对厚厚的Datasheet和复杂的原理图&#xff0c;很多新手工程师都会感到无从下手。本文将带你系统性地梳理整个Sensor调试流程&#xff0c;从关键参数提取到问题排查&#…...

量子网络远程纠缠生成技术及其应用

1. 量子网络中的远程纠缠生成技术解析量子纠缠作为量子计算与量子通信的核心资源&#xff0c;其非局域特性为分布式系统提供了经典方法无法实现的协调能力。在金融高频交易、智能电网调度等对延迟极度敏感的领域&#xff0c;量子纠缠带来的协调优势尤为显著。基于腔量子电动力学…...