手写一个JVM自定义类加载器

1. 自定义类加载器的意义

  • 隔离加载类:在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如:阿里内某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。再比如:Tomcat这类Web应用服务器,内部自定义了好几种类加载器,用于隔离同一个Web应用服务器上的不同应用程序。 (类的仲裁–>类冲突)
  • 修改类加载的方式:类的加载模型并非强制,除Bootstrap外,其他的加载并非一定要引入,或者根据实际情况在某个时间点进行按需进行动态加载
  • 扩展加载源:比如从数据库、网络、甚至是电视机机顶盒进行加载。
  • 防止源码泄漏:Java代码容易被编译和篡改,可以进行编译加密。那么类加载也需要自定义,还原加密的字节码。

2. 手写一个自定义类加载器

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;/*** @author polaris* @version 1.0* ClassName UserClassLoader* Package main.java.classloader* Description* @create 2024-07-21 20:06*/
public class CustomerClassLoader extends ClassLoader{private String rootPath;public CustomerClassLoader (String rootPath){this.rootPath = rootPath;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String filePath = rootPath + "\\" + name.replace(".", "\\") + ".class";//获取指定路径的class文件对应的二进制流数据byte[] data = getBytesFromPath(filePath);return defineClass(name,data,0,data.length);}public byte[] getBytesFromPath(String path){FileInputStream fis = null;ByteArrayOutputStream baos = null;try {fis = new FileInputStream(path);baos = new ByteArrayOutputStream();byte[] buffer = new byte[1024];int len;while((len=fis.read(buffer))!=-1){baos.write(buffer,0,len);}return baos.toByteArray();} catch (IOException e) {e.printStackTrace();} finally {try {assert fis != null;fis.close();} catch (IOException e) {e.printStackTrace();}try {assert baos != null;baos.close();} catch (IOException e) {e.printStackTrace();}}return new byte[0];}public static void main (String[] args){try {String rootDir = "G:\\JavaLearning2023\\20_JVMJava虚拟机\\尚硅谷宋红康JVM精讲与GC调优\\JVMdachang\\chapter02_classload\\src";//######################自定义类加载器1CustomerClassLoader loader1 = new CustomerClassLoader(rootDir);Class<?> clazz1 = loader1.loadClass("com.atguigu.java3.User");System.out.println("----------自定义类加载器1----------");System.out.println("获取到的Class1实例:"+clazz1);System.out.println("Class1的类加载器:"+clazz1.getClassLoader());System.out.println("类加载器的父类加载器:"+clazz1.getClassLoader().getParent());Class<?> clazz11 = loader1.findClass("com.atguigu.java3.User");System.out.println("获取到的Class11实例:"+clazz11);System.out.println("Class11的类加载器:"+clazz11.getClassLoader());System.out.println("类加载器的父类加载器:"+clazz11.getClassLoader().getParent());//######################自定义类加载器2CustomerClassLoader loader2 = new CustomerClassLoader(rootDir);Class clazz2 = loader2.findClass("com.atguigu.java3.User");System.out.println("----------自定义类加载器2----------");System.out.println("获取到的Class2实例:"+clazz2);System.out.println("Class2的类加载器:"+clazz2.getClassLoader());System.out.println("类加载器的父类加载器:"+clazz2.getClassLoader().getParent());System.out.println("clazz1==clazz2 :" + (clazz1 == clazz2));//clazz1与clazz2对应了不同的类模板结构。System.out.println("clazz11==clazz2 :" + (clazz11 == clazz2));//clazz11与clazz2对应了不同的类模板结构。Class clazz22 = loader2.loadClass("com.atguigu.java3.User");System.out.println("获取到的Class22实例:"+clazz22);System.out.println("Class22的类加载器:"+clazz22.getClassLoader());System.out.println("类加载器的父类加载器:"+clazz22.getClassLoader().getParent());System.out.println("clazz22==clazz2 :" + (clazz22 == clazz2));//clazz22与clazz2对应了相同的类模板结构。//######################自定义类加载器3CustomerClassLoader loader3 = new CustomerClassLoader(rootDir);Class<?> clazz3 = loader3.loadClass("com.atguigu.java3.User");System.out.println("----------自定义类加载器3----------");System.out.println("获取到的Class3实例:"+clazz3);System.out.println("Class3的类加载器:"+clazz3.getClassLoader());System.out.println("类加载器的父类加载器:"+clazz3.getClassLoader().getParent());System.out.println("clazz1==clazz3 :" + (clazz1 == clazz3));//clazz1与clazz3对应了相同的类模板结构。//######################系统类加载器System.out.println("----------系统类加载器----------");Class clazz4 = ClassLoader.getSystemClassLoader().loadClass("com.atguigu.java3.User");System.out.println("获取到的Class4实例:"+clazz4);System.out.println("Class4的类加载器:"+clazz4.getClassLoader());System.out.println("类加载器的父类加载器:"+clazz4.getClassLoader().getParent());System.out.println("clazz1==clazz4 :" + (clazz1 == clazz4));//clazz1与clazz4对应了相同的类模板结构。//######################new对象的加载器System.out.println("----------new对象的加载器----------");com.atguigu.java3.User user = new com.atguigu.java3.User();Class<? extends com.atguigu.java3.User> clazz5 = user.getClass();System.out.println("获取到的Class5实例:"+clazz5);System.out.println("Class5的类加载器:"+clazz5.getClassLoader());System.out.println("类加载器的父类加载器:"+clazz5.getClassLoader().getParent());System.out.println("clazz1==clazz5 :" + (clazz1 == clazz5));//clazz1与clazz5对应了相同的类模板结构。} catch (ClassNotFoundException e) {throw new RuntimeException(e);}}
}
输出
----------自定义类加载器1----------
获取到的Class1实例:class com.atguigu.java3.User
Class1的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d
获取到的Class11实例:class com.atguigu.java3.User
Class11的类加载器:com.atguigu.java3.CustomerClassLoader@7ea987ac
类加载器的父类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
----------自定义类加载器2----------
获取到的Class2实例:class com.atguigu.java3.User
Class2的类加载器:com.atguigu.java3.CustomerClassLoader@29453f44
类加载器的父类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
clazz1==clazz2 :false
clazz11==clazz2 :false
获取到的Class22实例:class com.atguigu.java3.User
Class22的类加载器:com.atguigu.java3.CustomerClassLoader@29453f44
类加载器的父类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
clazz22==clazz2 :true
----------自定义类加载器3----------
获取到的Class3实例:class com.atguigu.java3.User
Class3的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d
clazz1==clazz3 :true
----------系统类加载器----------
获取到的Class4实例:class com.atguigu.java3.User
Class4的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d
clazz1==clazz4 :true
----------new对象的加载器----------
获取到的Class5实例:class com.atguigu.java3.User
Class5的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
类加载器的父类加载器:sun.misc.Launcher$ExtClassLoader@4b67cf4d
clazz1==clazz5 :true

说明:

在自定义类加载器1中

用**loadClass()**方法加载,虽然是自定义类加载器,但是还是用的是系统类加载器AppClassLoader,其父类加载器是扩展类ExtClassLoader,这是因为基于双亲委派机制,会先向上层父类加载器请求加载,这里AppClassLoader正好管理了classpath下的所有类,就加载了User类,就没有loader1什么事了。

用**findClass()**方法加载,跳过了双亲委派机制,直接用当前类加载器加载,所以是CustomerClassLoader,其父类加载器是AppClassLoader。

而在自定义类加载器2中

用**findClass()**方法加载跳过了双亲委派机制,直接用当前类加载器加载,所以是CustomerClassLoader,其父类加载器是AppClassLoader。虽然都是自定义类加载器,但是是不同的类加载器实例,所以获取到Class实例clazz11和clazz2不同,clazz1和clazz就更不等了。

用**loadClass()**方法加载,会先检查是否已经被加载过了,发现已经用CustomerClassLoader加载过了,就会直接返回clazz2。

在自定义类加载器3中

还是用**loadClass()**方法加载,会先检查是否已经被加载过了,还没有加载过,根据双亲委派机制用AppClassLoader加载,但系统类加载器共用了clazz1的,已经加载过直接返回实例,所以clazz3和clazz1相同。

在系统类加载器中

用loadclass()方法加载,会先检查是否已经被加载过了,系统类加载器共用了clazz1的,已经加载过直接返回实例,所以clazz4和clazz1相同。

new的对象的加载器

由于new的时classpath下的类,所以默认用系统类加载器,系统类加载器共用了clazz1的,已经加载过直接返回实例,所以clazz5和clazz1相同。

注意:不同的类加载器实例的缓存(是否已经加载过)是不共通的,所以clazz4并不会等于clazz2,而系统类加载器和扩展类加载器会公用一个,加载过就不会再次加载,直接返回(所以说类很难卸载,系统类加载器会有很多引用)。

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

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

相关文章

Android lmkd机制详解

目录 一、lmkd介绍 二、lmkd实现原理 2.1 工作原理图 2.2 初始化 2.3 oom_adj获取 2.4 监听psi事件及处理 2.5 进程选取与查杀 2.5.1 进程选取 2.5.2 进程查杀 三、关键系统属性 四、核心数据结构 五、代码时序 一、lmkd介绍 Android lmkd采用epoll方式监听linux内…

SpringBoot整合阿里云短信业务

详细介绍SpringBoot整合阿里云短信服务的每一步过程&#xff0c;同时会将验证码存放到Redis中并设置过期时间&#xff0c;尽量保证实战的同时也让没做过的好兄弟也能实现发短信的功能~ 1. 注册阿里云账号和创建Access Key 首先&#xff0c;你需要注册一个阿里云账号&#xff0…

Flutter 使用 url_launcher的canLaunchUrl() 方法总是返回false错误

Flutter 使用 url_launcher的canLaunchUrl() 方法总是返回false错误 众所周知&#xff0c;我们一般使用url_launcher来打开各种应用&#xff0c;网页&#xff0c;手机应用等.... 但是最近发现Flutter的canLaunchUrl()方法总是返回false&#xff0c;这是为什么呢&#xff1f; …

Qt 实战(3)数据类型 | 3.2、QVariant

文章目录 一、QVariant1、存储数据1.1、存储Qt内置数据1.2、存储自定义数据 2、获取数据3、判断数据类型4、清空数据5、总结 前言&#xff1a; QVariant是Qt框架中一个非常强大且灵活的类&#xff0c;它提供了一种通用的方式来存储和转换几乎任何类型的数据。无论是基本数据类型…

【JavaEE初阶】Thread类及常见方法

目录 &#x1f4d5; Thread类的概念 &#x1f4d5; Thread 的常见构造方法 &#x1f4d5; Thread 的几个常见属性 &#x1f4d5; start()-启动一个线程 &#x1f4d5; 中断一个线程 &#x1f6a9; 实例一 &#x1f6a9; 实例二 &#x1f6a9; 实例三 &#x1f4d5; jo…

Android中的usescleartexttraffic属性详解

Android中的usescleartexttraffic属性详解 usesCleartextTraffic 是 Android 应用程序开发中的一个重要配置选项&#xff0c;用于控制应用程序是否允许通过不加密的 HTTP 协议进行网络通信。在 Android 应用的开发过程中&#xff0c;正确地配置 usesCleartextTraffic 对于保护用…

昇思MindSpore学习入门-数据处理管道支持python对象

数据处理管道中的特定操作&#xff08;如自定义数据集GeneratorDataset、自定义map增强操作、自定义batch(per_batch_map...)&#xff09;支持任意Python类型对象作为输入。为了支持此特性&#xff0c;数据管道使用了Python(dict)字典去管理不同类型的对象。与其他类型相比&…

康康近期的慢SQL(oracle vs 达梦)

近期执行的sql&#xff0c;哪些比较慢&#xff1f; 或者健康检查时搂一眼状态 oracle&#xff1a; --最近3天内的慢sql set lines 200 pages 100 col txt for a65 col sql_id for a13 select a.sql_id,a.cnt,a.pctload,b.sql_text txt from (select * from (select sql_id,co…

Nvm和Npm和Pm2的关系和使用说明

一、三者关系说明 nvm、npm 和 pm2 在 Node.js 生态系统中扮演着不同的角色&#xff0c;但它们之间存在一定的关联。下面是每个工具的作用以及它们之间的关系&#xff1a;1. nvm (Node Version Manager)• nvm 是一个用于管理多个 Node.js 版本的工具。它允许用户在不同的项目…

基于微信小程序的自习室选座系统/基于Java的自习室选座系统/自习室管理系统的设计与实现

获取源码联系方式请查看文章结尾&#x1f345; 摘要 自习室选座是学校针对用户必不可少的一个部分。在学校的整个过程中&#xff0c;学生担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类微信小程序自习室选座也在不断改进。本课题所设计的小程序自习室选座系…

【C#】Visual Studio2022打包依赖第三方库的winForm程序为exe

0.简介 IDE&#xff1a;VS2022 平台&#xff1a;C# .NetFramework4.7.2 WinForm界面 有GDAL、EEplus第三方库的依赖&#xff0c;所以在其他未安装环境的电脑中功能无法使用。 1. 安装 1.1 运行文件输出 在VS扩展中选择管理扩展&#xff0c;安装&#xff1a;Microsoft Visua…

SpringBoot上传超大文件导致OOM,完美问题解决办法

问题描述 报错: Caused by: java.lang.OutOfMemoryError at java.io.ByteArrayOutputStream.hugeCapacity(ByteArrayOutputStream.java:123) ~[?:1.8.0_381] at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:117) ~[?:1.8.0_381] at java.…

Android 更换applicationId 后 微信没有回调

1、解决办法&#xff08;代码如下&#xff09;&#xff1a; 使用 <activity-alias>: 这是一个用于定义活动别名的元素。活动别名可以让您为已经定义的活动提供一个别名&#xff0c;从而可以通过别名启动原来的活动。 <activityandroid:name".wxapi.WXEntryActiv…

ES6 class 类

普通使用原型添加方法 function Animal(name) {this.name name; }Animal.prototype.speak function() {console.log(this.name makes a noise.); };function Dog(name) {Animal.call(this, name); }Dog.prototype Object.create(Animal.prototype); Dog.prototype.constr…

MQTTX连接华为云IoTDA

目录 华为IoTDA平台 MQTTX连接参数的设置 物模型的构建 属性上报 基本数据格式 时戳 我以前上课都是用巴法云服务器来演示MQTT的&#xff0c;前几天因为测试工业互联网关使用了华为的IoTDA&#xff0c;觉得也不算太复杂&#xff0c;今天尝试用MQTTX连接华为云&#xff0c…

【ARM】MDK-STM32g0xx.h文件与Define规则记录

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 记录问题STM32g0xx.h等有关ST的可读文件&#xff0c;尽量勿修改文件格式及对其代码进行添加和删减&#xff0c;记录查找问题的过程中的疑惑&#xff0c;并如何给予客户正确的回复&#xff0c;帮助销售完成验收&…

CefSharp音视频编译与免费下载

注&#xff1a;Cefharp 音频和视频播放编译&#xff0c;生成相应的dll文件&#xff0c;从而支持项目开发。 建议编译至少 16G 的 RAM和至少 250G 的 SSD。该脚本以 E 盘为例&#xff0c;您需要在 E 盘上手动创建 cef 文件夹。禁止在转载后通过发布其他平台向用户收取下载费用。…

C++笔记5

目录 图的基础问题 图上的环 无向图的环 DAG图与拓扑排序 拓扑排序 卡恩算法&#xff08;BFS&#xff09; 算法描述 统计图中每个点的入度&#xff08;即连向该点的边数&#xff09; 拓扑排序的DFS算法 算法描述 拓扑排序的DFS的实现简单&#xff0c;从一个入度为0的…

新形势下职业教育大数据人才培养策略

一、引言 随着信息技术的飞速发展&#xff0c;大数据已成为驱动经济社会变革的关键力量。在新形势下&#xff0c;职业教育作为技术技能人才培养的重要阵地&#xff0c;面临着如何适应大数据时代要求、提升人才培养质量的紧迫任务。当前&#xff0c;职业教育在大数据人才培养方…

云HIS系统源码,业务云协同和数据云协同的数字化医院信息系统

云HIS是利用云计算、大数据、物联网、移动互联网等技术&#xff0c;打造的一个安全、便捷、共享、协同的医疗互联网云生态平台。核心功能是业务云协同和数据云协同。云HIS具有可扩展、易共享、易协同、低成本、体验号、更便捷、易维护的优势&#xff0c;重新定义了数字化医院信…