JVM【Java虚拟机】基础知识(五)

ops/2024/12/27 17:14:07/

1. 双亲委派机制

由于Java虚拟机中有多个类加载器,双亲委派机制的核心是解决一个类到底由谁加载的问题。

💡双亲委派机制有什么用?

1.保证类加载的安全性

通过双亲委派机制避免恶意代码替换JDK中的核心类库,比如java.lang.String,确保核心类库的完整性和安全性。

2.避免重复加载

双亲委派机制可以避免同一个类被多次加载。

双亲委派机制指的是:当一个类加载器接收到加载类的任务时,会自底向上查找是否加载过,再由顶向下进行加载

在这里插入图片描述

☘每个类加载器都有一个父类加载器,在类加载的过程中,每个类加载器都会先检查是否已经加载了该类,如果已经加载则直接返回,否则会将加载请求委派给父类加载器。

在这里插入图片描述

☘如果所有的父类加载器都无法加载该类,则由当前类加载器自己尝试加载。所以看上去是自顶向下尝试加载。

在这里插入图片描述

第二次再去加载相同的类,仍然会向上进行委派,如果某个类加载器加载过就会直接返回。

在这里插入图片描述

向下委派加载起到了一个加载优先级的作用。

☘案例:

com.itheima.my.C这个类在当前程序的classpath中,看看是如何加载的。

首先自底向上一层一层的查找,发现都没有被加载过:

在这里插入图片描述

之后再自定向下开始加载,每一级类加载器检查该类是不是在它的加载路径中:

在这里插入图片描述

💡问题1:如果一个类重复出现在三个类加载器的加载位置,应该由谁来加载?
启动类加载器加载,根据双亲委派机制,它的优先级是最高的。

💡问题2:在自己的项目中去创建一个java.lang.String类,会被加载吗?

不能,会返回启动类加载器加载在rt.jar包中的String类

💡问题3:在Java中如何使用代码的方式去主动加载一个类呢?
方式1:使用Class.forName方法,使用当前类的类加载器去加载指定的类。
方式2:获取到类加载器,通过类加载器的loadClass方法指定某个类加载器加载。

java">//获取main方法所在类的类加载器,应用程序类加载器
ClassLoader classLoader = Demo1.class.getClassLoader();
//使用应用程序类加载器加载com.itheima.my.A
classLoader.loadClass("com.itheima.my.A");

☘细节:

每个Java实现的类加载器中保存了一个成员变量叫"父”(Parent)类加载器,可以理解为它的上级,并不是继承关系。

在这里插入图片描述

🐟注:启动类加载器是使用C++编写,没有父类加载器。

在Arthas中可以通过classloader -t 查看类加载器的父子关系。

☀面试题(高频)

类的双亲委派机制是什么?
1、当一个类加载器去加载某个类的时候,会自底向上向父类查找是否加载过,如果加载过就直接返回,如果一直到最顶层的类加载器都没有加载,再由顶向下进行加载。
2、应用程序类加载器的父类加载器是扩展类加载器,扩展类加载器的父类加载器是启动类加载器。
3、双亲委派机制的好处有两点:第一是避免恶意代码替换JDK中的核心类库,比如java.lang.String,确保核心类库的完整性和安全性。第二是避免一个类重复地被加载。

2.打破双亲委派机制

打破双亲委派机制的三种方式:

  1. 自定义类加载器

    自定义类加载器并且重写loadC1ass方法,就可以将双亲委派机制的代码去除。Tomcat通过这种方式实现应用之间类隔离。

  2. 线程上下文类加载器

    利用上下文类加载器加载类,比如JDBC和JNDI等。

  3. Osgi框架的类加载器

    历史上Osgi框架实现了一套新的类加载机制,允许同级之间委托进行类的加载。

自定义加载器

​ 一个Tomcat程序中是可以运行多个Web应用的,如果这两个应用中出现了相同限定名的类,比如Servlet类,Tomcat要保证这两个类都能加载并且它们应该是不同的类。
​ 如果不打破双亲委派机制,当应用类加载器加载Web应用1中的MyServlet之后,Web应用2中相同限定名的MyServlet类就无法被加载了。

如何解决?

Tomcat使用了自定义加载器来实现应用之间类的隔离,每一个应用会有一个独立的类加载器加载对应的类。

在这里插入图片描述

🐟先来分析ClassLoader的原理,ClassLoader中包含了4个核心方法。双亲委派机制的核心代码就位于loadClass方法中。

在这里插入图片描述

通过上面的分析可知,要想打破类的双亲委派机制,核心就是修改loadClass方法,改变其规则。

通过阅读双亲委派机制的核心代码,分析如何通过自定义的类加载器打破双亲委派机制。

打破双亲委派机制的核心就是将下边这一段代码重新实现。

java">//parent等于null说明父类加载器是启动类加载器,直接调用findBootstrapClassOrNull
//否则调用父类加载器的加载方法
if (parent != null){c=parent.loadClass(name,false);
}else{c=findBootstrapClassOrNull(name);
}
//父类加载器爱莫能助,我来加载!
if (c == null)c = findClass(name);

💧示例:

通过修改loadClass方法来自定义类加载器:

核心代码如下:

java">public class BreakClassLoader1 extends ClassLoader{//重写的loadClass方法@Overridepublic Class<?> loadClass(String name) throws ClassNotFoundException{//Object类交给父类加载器来加载if(name.startsWith("java.")){return super.loadClass(name);}byte[] data = loadClassData(name);return defineClass(name,data,0,data.length);}//main方法public static void main(String[] args) throws ClassNotFoundException,InstantiationException,IllegalAccessException{BreakClassLoader1 classLoader1 = new BreakClassLoader1();classLoader1.setBasePath("D:\\lib\\");Class<?> clazz1 = classLoader1.loadClass("com.itheima.my.A");System.out.println(clazz1.getClassLoader());//自定义加载器的父类加载器ClassLoader parent = classLoader1.getParent();System.out.println(parent);//结果是应用程序类加载器}
}

💡思考:

在这里插入图片描述

以Jdk8为例,ClassLoader类中提供了构造方法设置parent的内容:

在这里插入图片描述

这个构造方法由另一个构造方法调用,其中父类加载器由getSystemClassLoader方法设置,该方法返回的是AppClassLoader。

在这里插入图片描述

💡思考:

在这里插入图片描述

📕综上:

正确的去实现一个自定义的类加载器的方式是重写findClass方法,这样不会破坏双亲委派机制。

线程上下文类加载器

这里以JDBC为例:

JDBC中使用了DriverManager来管理项目中引入的不同数据库的驱动,比如mysql驱动、oracle驱动。

DriverManager类位于rt.jar包中,由启动类加载器加载。

依赖中的mysq驱动对应的类,由应用程序类加载器来加载。

📕因此,这里面产生了一个冲突:DriverManager属于rt.jar是启动类加载器加载的。而用户jar包中的驱动需要由应用类加载器加载,这就违反了双亲委派机制。

在这里插入图片描述

💡这里首先有一个问题:DriverManager怎么知道jar包中要加载的驱动在哪儿?

这里面需要引入一个机制:SPI机制

spi全称为(Service Provider Interface),是JDK内置的一种服务提供发现机制。

spi的工作原理:

1.在ClassPath路径下的META-lNF/services文件夹中,以接口的全限定名来命名文件名,对应的文件里面写该接口的实现。

2.使用ServiceLoader加载实现类。

在这里插入图片描述

DriverManage使用SPI机制,最终加载jar包中对应的驱动类。

💡SPI中是如何获取到应用程序类加载器的?
SPI中使用了线程上下文中保存的类加载器进行类的加载,这个类加载器一般是应用程序类加载器。

在这里插入图片描述

那问题又来了,是不是只要是创建一个线程就是应用类加载器进行加载呢?

java">public class NewThreadDemo{public static void main(String[] args){new Thread(new Runnable(){@Overridepublic void run(){	System.out.println(Thread.currentThread().getContextclassLoader());}}).start();}
}
//结果:就是应用类加载器
//sun.misc.Launcher$AppClassLoader@18b4aac2

💧总结:JDBC案例中真的打破了双亲委派机制吗?实际上是没有打破的,细节上每个类都是按照双亲委派机制来查找类加载器的。

在这里插入图片描述

Osgi框架的类加载器(了解)

历史上,OSGi模块化框架。它存在同级之间的类加载器的委托加载。OSG还使用类加载器实现了热部署的功能。

在这里插入图片描述


http://www.ppmy.cn/ops/145431.html

相关文章

大型语言模型(LLMs)演化树 Large Language Models

大型语言模型&#xff08;LLMs&#xff09;演化树 Large Language Models flyfish 下面的图来自论文地址 Transformer 模型&#xff08;如 BERT 和 GPT-3&#xff09;已经给自然语言处理&#xff08;NLP&#xff09;领域带来了革命性的变化。这得益于它们具备并行化能力&…

入侵他人电脑,实现远程控制(待补充)

待补充 在获取他人无线网网络密码后&#xff0c;进一步的操作是实现入侵他人电脑&#xff0c;这一步需要获取对方的IP地址并需要制作自己的代码工具自动化的开启或者打开对方的远程访问权限。 1、获取IP地址&#xff08;通过伪造的网页、伪造的Windows窗口、hook&#xff0c;信…

ES和MONGODB备份脚本

ES&#xff1a; 使用elasticdump备份&#xff1a; ###URL取svc: elasticsearch-masterd的 clusterip/9200 for item in $(curl http://$URL/_cat/indices | awk {print $3} | grep -E "要备份的索引名称" ) do echo mapping $item elasticdump --inputhttp://$…

数据结构二叉树

1.树概念及结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个层次关系的集合&#xff0c;把它叫做树是因为它看起来像一颗倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下 有一个特殊的结点&…

《Opencv》基础操作详解(2)

接上篇&#xff1a;《Opencv》基础操作详解&#xff08;1&#xff09;-CSDN博客 目录 Opencv基础操作 11、B、G、R颜色通道提取 12、显示单个通道颜色 13、 合并颜色通道 14、图像添加马赛克 15、图片区域替换 16、图片的缩放&#xff08;常用&#xff09; 17、图像运算…

PyCharm专项练习3 图的存储:邻接矩阵+邻接链表

一、实验目的 本文的实验目的是通过Python编程实践&#xff0c;实现对图的两种存储方式——邻接矩阵和邻接链表的掌握与应用。针对给定的有向图和无向图&#xff0c;通过编码实现这两种存储方式&#xff0c;并能够对图进行输出展示。 二、实验内容 图的存储方式实现&#xff…

37 Opencv SIFT 特征检测

文章目录 Ptr<SIFT> SIFT::create示例 Ptr SIFT::create Ptr<SIFT> SIFT::create(int nfeatures 0,int nOctaveLayers 3,double contrastThreshold 0.04,double edgeThreshold 10,double sigma 1.6 );参数说明&#xff1a;nfeatures&#xff1a;类型&#x…

计算机操作系统与安全复习笔记

1 绪论 操作系统目标: 方便性; 有效性; 可扩充性; 开放性. 作用: 用户与计算机硬件系统之间的接口; 计算机资源的管理者; 实现了对计算机资源的抽象; 计算机工作流程的组织者. 多道程序设计: 内存中同时存放若干个作业, 使其共享系统资源且同时运行; 单处理机环境下宏观上并行…