JAVA类加载机制(jdk8)

三句话总结JDK8的类加载机制:

  1. 类缓存:每个类加载器对他加载过的类都有一个缓存。
  2. 双亲委派:向上委托查找,向下委托加载。
  3. 沙箱保护机制:不允许应用程序加载JDK内部的系统类。

JDK8的类加载体系

在这里插入图片描述
类加载器的核心方法

//protected声明可以被子类覆盖
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);//没有父类加载器(表示parent是BootstrapClassLoad)就查询BootstrapClassLoad的缓存不存在则创建,但是BootstrapClassLoad只创建核心类库(java.lang.*、java.util.* 等,非核心类会返回空或者或抛异常)}} catch (ClassNotFoundException e) {}if (c == null) {//BootstrapClassLoaderlong t1 = System.nanoTime();// 父类加载起没有加载过,就自行解析class文件加载。c = findClass(name);sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}//这一段就是加载过程中的链接Linking部分,分为验证、准备,解析三个部分。// 运行时加载类,默认是无法进行链接步骤的。if (resolve) {resolveClass(c);}return c;}}

可以保护JDK内部的核心类不会被应用覆盖,因为本加载器缓存没有就会往父类加载器缓存找,核心类在bootstarp加载器缓存中存在。并且缓存相关代码是native无法直接被java代码操作
在这里插入图片描述

沙箱保护机制

private ProtectionDomain preDefineClass(String name,ProtectionDomain pd){if (!checkName(name))throw new NoClassDefFoundError("IllegalName: " + name);// 不允许加载核心类if ((name != null) && name.startsWith("java.")) {throw new SecurityException("Prohibited package name: " +name.substring(0, name.lastIndexOf('.')));}if (pd == null) {pd = defaultDomain;}if (name != null) checkCerts(name, pd.getCodeSource());return pd;}

Linking链接过程

在ClassLoader的loadClass方法中还有一个resolveClass,是一个native方法,其实现的过程称为linking-链接。
在这里插入图片描述

  • 加载:通过类加载器将class文件加载到jvm中(应用唯一可插手的步骤)
  • 验证:验证class规范
  • 准备:为静态变量分配内存赋默认值(例如int为0)
  • 解析:符号引用解析为直接引用
  • 初始化:静态变量赋值,执行类的静态代码块,初始化当前类的父类

Class.forName默认会直接进行Linking并初始化,ClassLoader.loadClass不会而是只加载(懒加载)

通过类加载器引入外部Jar包

public class OADemo2 {public static void main(String[] args) throws Exception {Double salary = 15000.00;Double money = 0.00;URL jarPath = new URL("file:/Users/roykingw/DevCode/ClassLoadDemo/out/artifacts/SalaryCaler_jar/SalaryCaler.jar");URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {jarPath});//模拟不停机状态while (true) {try {money = calSalary(salary,urlClassLoader);System.out.println("实际到手Money:" + money);}catch(Exception e) {e.printStackTrace();System.out.println("加载出现异常 :"+e.getMessage());}Thread.sleep(5000);}}private static Double calSalary(Double salary,ClassLoader classloader) throws Exception {Class<?> clazz = classloader.loadClass("com.roy.oa.SalaryCaler");if(null != clazz) {Object object = clazz.newInstance();return (Double)clazz.getMethod("cal", Double.class).invoke(object, salary);}return -1.00;}
}
  1. 哪些jar包适合放到外部加载?
    规则引擎、统一审批规则、订单状态规则,因为会直接加载到jvm,要考虑安全性

  2. 外部jar包可以放到哪些地方?
    URLClassLoader可以定义URL从远程Web服务器加载Jar包。
    drools规则引擎实现了从maven仓库远程加载核心规则文件。

自定义类加载器实现Class代码混淆

对class进行MD5、对称加密、非对称加密,类加载器再进行解密。同时把解密类加载器通过另一个加载器内网远程加载,或者u盘加载来实现Class代码混淆加密

自定义类加载器实现热加载

在 Java 中,类加载器一旦加载某个类,该类就无法被“卸载”(除非整个类加载器被 GC 回收)。所以热加载的关键是:每次都使用一个新的 ClassLoader 实例来加载 class 文件,不复用旧的 ClassLoader,否则类会复用旧版本

public class OADemo5 {public static void main(String[] args) throws Exception {Double salary = 15000.00;Double money = 0.00;//模拟不停机状态while (true) {try {money = calSalary(salary);System.out.println("实际到手Money:" + money);}catch(Exception e) {System.out.println("加载出现异常 :"+e.getMessage());}Thread.sleep(5000);}}private static Double calSalary(Double salary) throws Exception {SalaryJARLoader salaryClassLoader = new SalaryJARLoader("/Users/roykingw/lib/SalaryCaler.jar");//每次都创建新的ClassLoader实例来加载 class 文件,新的ClassLoader里当然没有缓存System.out.println(salaryClassLoader.getParent());Class<?> clazz = salaryClassLoader.loadClass("com.roy.oa.SalaryCaler");if(null != clazz) {Object object = clazz.newInstance();return (Double)clazz.getMethod("cal", Double.class).invoke(object, salary);}return -1.00;}
}

这种热加载机制需要创建出非常多的ClassLoader对象。而这些不用的ClassLoader对象加载过的缓存对象也会随之成为垃圾。这会让JVM中本来就不大的元数据区带来很大的压力,极大的增加GC线程的压力。

把SalaryJARLoader加载过的类打印出来,你会发现,在加载SalaryCaler时,其实不光加载了这个类,同时还加载了Double和Object两个类。这两个类哪里来的?这就是JVM实现的懒加载机制。JVM为了提高类加载的速度,并不是在启动时直接把进程当中所有的类一次加载完成,而是在用到的时候才去加载。也就是懒加载。

打破双亲委派,实现同类多版本共存

在这里插入图片描述
外部加载的jar包,使用自定义加载器,这时如果代码中使用一样的类,那么AppClassLoader就有了缓存,就不会走自定义加载器的逻辑了

tomcat打破双亲委派
在这里插入图片描述
tomcat的几个主要类加载器:

  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  • WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见,比如加载war包里相关的类,每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本,这样实现就能加载各自的spring版本;
  • Jsp类加载器:针对每个JSP页面创建一个加载器。这个加载器比较轻量级,所以Tomcat还实现了热加载,也就是JSP只要修改了,就创建一个新的加载器,从而实现了JSP页面的热更新。

使用类加载器能不能不用反射?

强行的类型转换会报错Exception in thread “main” java.lang.ClassCastException: com.roy.oa.SalaryCaler cannot be cast to com.roy.oa.SalaryCaler

JDK的SPI扩展机制
ServiceLoader.load(SalaryCalService.class) 就可以查找到某一个接口的全部实现类。应用所需要的,是提供一个配置文件。 这个配置文件需要放在 ${classpath}/META-INF/services这个固定的目录下。然后文件名是传入接口的全类名。而文件的内容则是一行表示一个实现类的全类名。

SPI机制也是传入了ClassLoader的。

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

SPI配置文件可以放入jar包中,但是必须要使用URLClassLoader,由于向上委托查找会找到AppClassLoader,那么我们可以通过构造函数设置它的parent为salaryJARLoader就可以实现了。还可以通过java -cp这个启动参数直接把jar包导入

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

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

相关文章

更进一步深入的研究ObRegisterCallBack

引入 我们如果想hook对象的回调,在上篇文章里我们已经知道了对象回调函数存在一个列表里面&#xff0c;我们通过dt可以看见&#xff0c;这里他是一个LIST_ENTRY结构&#xff0c;但是实际调用的时候&#xff0c;这样是行不通的&#xff0c;说明它结构不对 0: kd> dt _OBJEC…

Nginx-3 Nginx 的负载均衡策略

Nginx-3 Nginx 的负载均衡策略 Nginx 的负载均衡其实就是指将请求按照一定的策略转发给服务集群中的一台&#xff0c;提高了服务集群的可用性&#xff0c;解决数据流量过大、网络负荷过重的问题。 AKF 扩展立方体 分为 3 个方向负载&#xff1a; x 轴&#xff1a;增加实例数…

Wiiu平台RetroArch全能模拟器美化整合包v1.18

这款WiiU平台RetroArch全能模拟器美化整合包v1.18的亮点包括&#xff1a; 1. 18款平台完美兼容&#xff1a;无论你是喜欢NES时代的经典游戏&#xff0c;还是钟爱SNES、GBA等平台的大作&#xff0c;这款整合包都能满足你的需求&#xff0c;让你尽情畅玩游戏。 2. 三款自制主题&a…

MyBatis原理

Mybatis执行过程为&#xff1a;接口代理->sqlSession会话->executor执行器->JDBC操作 一、接口代理 Mybatis根据Mapper接口&#xff0c;动态生成相应实现类 二、SqlSession介绍 MyBatis核心对象SqlSession介绍 - MyBatis中文官网 三、Executor执行器介绍 精通My…

升级内核4.19-脚本

#bash cd /root yum remove -y kernel-tools-3.10.0-1160.el7.x86_64 yum remove -y kernel-tools-libs-3.10.0-1160.el7.x86_64tar -xvf rhel-7-amd64-rpms.tar.gz cd /root/rhel-7-amd64-rpms #安装依赖、包括socat&conntrack yum localinstall -y *.rpm --skip-broken#升…

全面理解 JVM 垃圾回收(GC)机制:原理、流程与实践

JVM 的 GC&#xff08;Garbage Collection&#xff09;机制是 Java 程序性能的关键支柱。本文将从堆内存布局、回收原理、GC 算法、流程细节、并发收集器机制等维度&#xff0c;系统讲清楚 GC 的底层运作原理和优化思路。 一、JVM 堆内存结构 Java 堆是 GC 管理的主要区域&am…

runas命令让其他用户以管理员权限运行程序

RUNAS 用法: RUNAS使用示例&#xff1a; runas /noprofile /user:mymachine\administrator cmd #本机Administrator管理员身份执行CMD&#xff0c;/noprofile为不加载该用户的配置信息。runas /profile /env /user:mydomain\admin “mmc %windir%\system32\dsa.msc” #本机上…

实战指南:部署MinerU多模态文档解析API与Dify深度集成(实现解析PDF/JPG/PNG)

MinerU web api部署 MinerU 能够将包含图片、公式、表格等元素的多模态 PDF、PPT、DOCX 等文档转化为易于分析的 Markdown 格式。 克隆 MinerU 的仓库 git clone https://github.com/opendatalab/MinerU.gitcd 到 projects/web-api cd projects/web-api在可以科学上网的情况下…

向量外积与秩1矩阵的关系

向量外积与秩1矩阵的关系 flyfish 向量外积是构造秩1矩阵的基本工具&#xff0c;其本质是用两组向量的线性组合刻画矩阵的行和列相关性&#xff1b;任意秩1矩阵必可表示为外积&#xff0c;而低秩矩阵&#xff08;秩 k k k&#xff09;可分解为 k k k 个外积矩阵的和&#x…

设计模式-创建型模式(详解)

创建型模式 单例模式 一个类只允许创建一个对象&#xff0c;称为单例。 单例体现&#xff1a;配置类、连接池、全局计数器、id生成器、日志对象。 懒汉式 (线程不安全) 单例&#xff1a;【不可用】 用到该单例对象的时候再创建。但存在很大问题&#xff0c;单线程下这段代…

什么是BI?有哪些应用场景

BI&#xff08;Business Intelligence&#xff0c;商业智能&#xff09;是通过技术手段对海量业务数据进行采集、整合、分析和可视化的过程&#xff0c;旨在帮助企业从数据中获取洞察&#xff0c;支持决策。其核心是通过工具&#xff08;如Quick BI&#xff09;将原始数据转化为…

从零开始:使用Vite和Vue.js搭建一个空项目

进入node.js官网 https://nodejs.org/zh-cn 下载node.js 点击进行安装&#xff0c; 完成之后&#xff0c;按住shift鼠标右键&#xff0c;打开powershell窗口 输入node -v &#xff0c;出现版本号就是成功了 node -v 接下来&#xff0c;打开设置&#xff0c;搜索开发者设置&…

Redis 核心数据类型及典型使用场景详解

在日常开发中&#xff0c;Redis 不仅是缓存利器&#xff0c;更是一套高性能的数据结构服务。你是否真的了解 Redis 提供的五种核心数据类型&#xff1f;它们各自的底层结构和适用场景又有哪些差异&#xff1f;本篇博客将深入解析 Redis 的数据类型及其典型应用&#xff0c;助你…

threejs webVR获取相机正前方向量

通常获取相机正前方可以使用camera.getWorldDirection(new Vector3()) 函数来得到&#xff0c;但是在threejs0.139.2版本中进入VR后使用上面函数获取的数据是固定不变的&#xff0c;不管是否旋转了头盔&#xff0c;经过一番研究发现必须使用renderer.xr.getCamera() 此函数获取…

华为OD-2024年E卷-字符统计及重排[100分] -- python

问题描述&#xff1a; 给出一个仅包含字母的字符串&#xff0c;不包含空格&#xff0c;统计字符串中各个字母(区分大小写)出现的次数&#xff0c;并按照字母出现次数从大到小的顺序输出各个字母及其出现次数。如果次数相同&#xff0c;按照自然顺序进行排序&#xff0c;且小写…

MCP(模型上下文协议)协议和Http协议对比

MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;和 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是两种定位完全不同的协议&#xff0c;主要区别如下&#xff1a; 1. 核心定位 HTTP 通用网络通信协议&am…

C++打印乘法口诀表

int main()​​&#xff1a; 这是C 程序的入口点。每个C 程序都必须有一个 main 函数&#xff0c;程序从这里开始执行。 ​​外层 for 循环​​&#xff1a; for (int i 1; i < 10; i) { int i 1&#xff1a;定义并初始化循环变量 i 为 1。这里的 i 代表乘法表中的行…

RoGBAG 与 MCAP

RoGBAG 和 MCAP 都是机器人领域常用的二进制数据格式&#xff0c;用于存储传感器数据、控制命令和状态信息。两者主要区别在于&#xff1a; RoGBAG&#xff1a;ROS 1/2 的标准日志格式&#xff0c;采用 LZF/LZ4 压缩&#xff0c;适合中小型数据集 MCAP&#xff1a;新一代机器人…

Ubuntu 空间占用情况排查常用命令

查看当前目录总大小及子目录占用详情 du -sh * | sort -hr ​​du​​&#xff1a;磁盘使用统计命令​​-s​​&#xff1a;显示每个参数的总计&#xff08;不递归子目录&#xff09;​​-h​​&#xff1a;以人类可读格式&#xff08;KB/MB/GB&#xff09;显示​​*​​&…