JVM类的加载相关的问题

JVM类的加载相关的介绍

学习类的加载的加载过程对深入理解JVM有十分重要的作用,下面就跟我一起学习JVM类的加载过程吧!


文章目录

  • JVM类的加载相关的介绍
  • 一、类的加载过程
  • 二、双亲委派机制
    • 1、类加载器的种类
    • 2、为什么JVM要分成不同的类的加载器
    • 3、类的加载过程
    • 4、为什么要通过双亲委派机制
    • 5、从代码层次分析双亲委派机制
  • 三、自定义类加载器
  • 四、打破双亲委派机制
    • 1.tomcat打破双亲委派机制
    • 2.SPI 打破双气双亲委派机制


一、类的加载过程

JVM的类的加载过程分为五个阶段:加载、验证、准备、解析、初始化
加载
  加载阶段就是将编译好的的class文件通过字节流的方式从硬盘或者通过网络加载到JVM虚拟机当中来。(我们平时在Idea中书写的代码就是放在磁盘中的,也可以通过网络加载到虚拟机)

验证
  验证阶段主要是验证class文件的格式或者内容是否符合java规范和虚拟机规范,比如最常见的字节码文件中的cafe baby,以及通过class文件是u2或者u4的的方式解析字节码中各部分的内容是否符合规范。

准备
  准备阶段就是给静态变量(static修饰的变量)赋默认值的过程,int类型的赋值为0,对象类型赋值为null,这都是默认值,需要注意的是常量,也就是被final修饰的static,在类编译的时候就已经赋值完成了。

解析
 解释阶段做的事情用一句话来说就是将符号引用转变为直接引用(句柄)
 句柄:如下如所示虚拟机有两种引种对象数据的方式,一种是通过句柄池的方式,一种是通过直接指针的方式,句柄池的方式就是在引用和真实地址之间多了一层句柄,这样的好处就是当引用的真实对象发生改变时,栈中存放的值不需要改动,但是多了一层就会增加系统的复杂性,所以hotsport虚拟机用的是直接地址指针的方式(如下图)。
直接引用:数据在内存中的真实地址
符号引用: 包括一些方法名和字面量等,这些符号引用在加载过程中有的可以确定下来的,比如static修饰的main方法,这些可以确定下来的引用在解析过程中就会转变为指向真实地址的指针,还有一些是只有在实际运行期间才可以确定下来的,比如多态,接口等,这些就需要到真正调用的时候才转为直接地址引用。(这也就是静态连接和动态连接的区别)

在这里插入图片描述

初始化
类的初始化是将给静态变量赋值(这时候的赋值才是赋代码中书写的值)静态代码块收集在一起进行执行的过程(其实就是执行字节码中的clinit方法,注意这个方法不是类的构造方法)执行顺序根据静态变量和静态代码块在类中的顺序决定。代码如下

public class Hello {//准备阶段给a赋值为0.初始化的时候给a赋值为3public static int a=3;//根据顺序先执行a=3,在实行静态方法static {String b="b";}public static void main(String[] args) {}}

注意:类的初始化,实例化对象


二、双亲委派机制

双亲委派机制是我们学习类的加载和面试过程中逃不掉的问题,接下来我会通过介绍类加载器的种类,jvm虚拟机使通过什么方式调用这些类加载器完成类的加载的,双亲委派机制是什么,打破双亲委派机制的例子有哪些。

1、类加载器的种类

类的加载过程,同通过类加载器实现的。类的加载器主要有如下几种:
引导类加载器:c++代码实现,负责接在java的核心类库,比如rt.jar等
扩展类加载器:加载java的一些扩展类,负责加载jre下ext扩展目录下的jar报
应用类加载器:负责加载classpath下的类,也就是我们自己写的类
自定义类加载器:我们自己写的写的类加载器,负责加载自定义路径下的类

2、为什么JVM要分成不同的类的加载器

介绍为什么要搞这么多类加载器之前,就需要先说明JVM是如何使用这些类加载器的(也就是双亲委派机制)。双亲委派机制就是(引导类加载器->扩展器类加载器->应用类加载器->自定义类加载器 逐层上层是下层类加载器的父亲,注意这里不同于extends的继承关系,是类加载器中有一个parent属性,指向它的父亲是谁)

在这里插入图片描述

在这里插入图片描述

3、类的加载过程

抛开自定义加载器先不谈,当我们自己编的类加载的时候,会先去应用类加载器中查找该类是否已经加载了(如果加载了直接返回),如果没有则向上委托扩展类加载器看它是否已经加载(加载了直接返回),如果没有则继续向上委托(加载了直接返回),若没有加载并且是需要加载核心JAVA的核心类库,则加载如果不是核心类库,则通知下层去加载,如果是JAVA的扩展包的类就通知扩展类加载器加载,如果是用户自定义的类就再由扩展类加载器继续向下通知应用程序加载器加载。这就是JVM的双亲委派机制。

4、为什么要通过双亲委派机制

JVM为什么要通过这种麻烦的方式,来实现类的加载的,原因如下:

  • 沙箱机制:JAVA不希望用户或者黑客随便更改核心类库,所以再加载核心类库和扩展包的时候,JAVA要保证加载的是自己核心类库的东西,而不是别人更改过的类。,就是为了安全
  • 避免了类的重复加载

5、从代码层次分析双亲委派机制

JAVA的双亲委派的机制的主要概念:

  • 引导类加载器由JAVA虚拟机启动以后,调用底层C++代码加载
  • 扩展类加载器和应用类加载器由一个被称为启动器的加载,该类就是Launcher, 扩展类加载器和应用类加载器的上下层父子关系由parent属性确立
  • Launcher类是由引导类加载器加载的

类的关系图

AppClassLoader和ExtClassLoader都是Launcher(启动器)的内部类,由Launcher调用构造方法的时候,创建AppClassLodader和ExtClassLoder
在这里插入图片描述
                      图一

图一
                     图二

AppClassLoader和ExtClassLoder都是继承自URLClassLoader

在这里插入图片描述在这里插入图片描述

URLClassLoader最终继承自ClassLoader

在这里插入图片描述
                     图三
                     
类的父子关系整理清楚了,下面看Launcher是怎么创建AppcClassLloader和ExtClassLoader的?
可以看到,在创建AppClassLoader时,将创建好的ExtClassLoader传给了AppClassLoader的get**方法。这个方法最终直接会调用到它的构造方法,我们直接看它的构造放的代码。
在这里插入图片描述
                     图四

我们看下图AppClassLoader的构造方法,会调用super,也就是父类的构造方法,根据图三的类的关系图,会最终调用到ClassLoader类的构造方法,我们继续进入到它的构造方法中看它干了什么。
在这里插入图片描述
                     图五

好的我们看到了parent属性,也就是将最初传入的ExtClassLoader的实例设置为APPClassLoader的父亲。
tuliu在这里插入图片描述
                    图六

总结:引导类加载器加载了启动器Launcher,Launcher创建了AppClassLoader和ExtClasslLoader,并好父子关系(通过parent)


你以为这就完了吗?,不可能,绝对不可能!双亲委派的代码还没说呢,加载类之前向上委托那一系列的操作,在哪能还没说呢,怎么可能就这么结束了呢!!!


我们来看类是怎么加载的,我们都知道,加载一个类的时候我们会调用ClassLoader的loaderClass方法,那我们就看看父子关系建立好以后,是怎么实现向上委托的?
在这里插入图片描述
                     图七

 public Class<?> loadClass(String name) throws ClassNotFoundException {//调用了重载的loadClass方法,多传入一个flasereturn loadClass(name, false);}
 protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{//加锁是为了保证加载类class文件时,必须是安全的,不会因为并发加载多个类进来synchronized (getClassLoadingLock(name)) {// 先根据类的全限定名查找类是否已经加载过了,加载过了c!=null直接返回。不走下面的代码Class<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {/*** 注意:这里就是双亲委派的关键代码*代码走到这里会让他的parent去递归调用本方法,直到parent==null时(ExtClassload),这里就符合双亲委派机制,向上委托*///AppClassLoader执行这行代码if (parent != null) {c = parent.loadClass(name, false);//扩展类加载器执行这行代码} else {//因为ExtClassLoader的partent==null执行这行代码,调用引导类加载器,加载java核心类库的代码返回c==null 代码继续向下执行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();//因为之前是递归调用的,第一次是ExtClassLoader调用这个方法,去加载java扩展包的类//第二次是AppClassLoader调用这个方法,AppCLassLoader是加载classpath路径下我们自己自己写的类,具体实现的方法在URLClassLoader,我们去看URLClassLoader是怎么实现的,如图八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;}}

在这里插入图片描述
                     图八

加载class流文件
在这里插入图片描述
                       图九

总结

  • 引导类加载器由底层C++代码创建
  • 引导类加载器加载启动器Launcher
  • Launcher的构造方法创建ExtClassLoade和AppClassLoader,并通过parent属性确认父子父子关系
  • ClassLoader的loadClass方式实现了双亲委派机制
  • findClass方法调用defindClass实现classpath下的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);//defineClass将一个字节数组转为Class对象,这个字节数组是class文件读取后最终的字节数组return defineClass(name, data, 0, data.length);} catch (Exception e) {e.printStackTrace();throw new ClassNotFoundException();}}}public static void main(String args[]) throws Exception {//初始化自定义类加载器,会先初始化父类ClassLoader,其中会把自定义类加载器的父加载器设置为应用程序类加载器AppClassLoaderMyClassLoader classLoader = new MyClassLoader("D:/test");//D盘创建 test/com/test/jvm 几级目录,将User类的复制类User1.class丢入该目录Class clazz = classLoader.loadClass("com.test.jvm.User1");Object obj = clazz.newInstance();Method method = clazz.getDeclaredMethod("sout", null);method.invoke(obj, null);System.out.println(clazz.getClassLoader().getClass().getName());}
}

四、打破双亲委派机制

1.tomcat打破双亲委派机制

2.SPI 打破双气双亲委派机制

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

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

相关文章

双碳目标下基于“遥感+”集成技术的碳储量、碳排放、碳循环、温室气体等多领域监测与模拟实践

卫星遥感具有客观、连续、稳定、大范围、重复观测的优点&#xff0c;已成为监测全球碳盘查不可或缺的技术手段&#xff0c;卫星遥感也正在成为新一代 、国际认可的全球碳核查方法。目的就是梳理碳中和与碳达峰对卫星遥感的现实需求&#xff0c;系统总结遥感技术在生态系统碳储量…

Type-C协议Ver2.0(学习笔记)

​​​​​​​1 简介 随着USB接口的持续成功&#xff0c;需要调整USB技术&#xff0c;以服务于新型计算平台和设备趋向于更小、更薄、更轻的外形。这些较新的平台和设备中的许多已经到了现有USB插座和插头阻碍创新的地步&#xff0c;特别是考虑到标准A和标准B版本USB连接器的…

信息化发展35

IT审计技术&#xff0d;风险评估技术 常用的IT 审计技术包括风险评估技术、审计抽样技术、计算机辅助审计技术及大数据审计技术。 IT 风险评估技术一般包括&#xff1a; 1 &#xff09;风险识别技术&#xff1a; 用以识别可能影响一个或多个目标的不确定性&#xff0c; 包括德…

STM32单片机OLED贪吃蛇游戏记分计时

实践制作DIY- GC00165---OLED贪吃蛇游戏 一、功能说明&#xff1a; 基于STM32单片机设计---OLED贪吃蛇游戏 二、功能说明&#xff1a; STM32F103C系列最小系统板0.96寸OLED显示器上、下、左、右4个按键 1.通过OLED配合按键实现贪吃蛇游戏 2.可以上下左右移动。 3.可以统计显…

【前端项目】博客系统(页面设计)

文章目录 一、预期效果二、实现博客列表页三、实现博客正文页四、实现博客登录页五、实现博客编辑页 一、预期效果 代码详情见&#xff1a;gitee链接 &#x1f495; 博客列表页效果 &#x1f495; 博客详情页效果 &#x1f495; 博客登录页效果 &#x1f495; 博客编辑页效果…

SpringBoot2.0(过滤器,监听器,拦截器)

目录 一&#xff0c;过滤器1.1&#xff0c;自定义Filter1.2&#xff0c;启动类代码1.2&#xff0c;创建filter类和LoginFilter包1.2.1&#xff0c;编写loginFilter类 过滤器代码1.2.2&#xff0c;创建二个Controller类 二&#xff0c;监听器2.1&#xff0c;自定义监听器2.2&…

如何查询成绩或工资

为什么每次查询成绩或者工资的时候都觉得麻烦又耗时呢&#xff1f;在过去&#xff0c;我们可能需要去学校或公司的相关部门&#xff0c;填写繁琐的表格&#xff0c;然后等待工作人员进行查询和处理。这不仅浪费了我们宝贵的时间&#xff0c;还可能出现查询结果不准确或者遗漏的…

【深度学习】实验09 使用Keras完成线性回归

文章目录 使用Keras完成线性回归1. 导入Keras库2. 创建数据集3. 划分数据集4. 构造神经网络模型5. 训练模型6. 测试模型7. 分析模型 附&#xff1a;系列文章 使用Keras完成线性回归 Keras是一款基于Python的深度学习框架&#xff0c;以Tensorflow、Theano和CNTK作为后端&#…

【视觉检测】电源线圈上的导线弯直与否视觉检测系统软硬件方案

 检测内容 线圈上的导线弯直与否检测系统。  检测要求 检测线圈上的导线有无弯曲&#xff0c;弯曲度由客户自己设定。检测速度5K/8H625PCS/H。  视觉可行性分析 对样品进行了光学实验&#xff0c;并进行图像处理&#xff0c;原则上可以使用机器视觉进行测试测量…

结构方程模型SEM、路径分析房价和犯罪率数据、预测智力影响因素可视化2案例...

原文链接&#xff1a;http://tecdat.cn/?p25044 在本文&#xff0c;我们将考虑观察/显示所有变量的模型&#xff0c;以及具有潜在变量的模型&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 1 简介 第一种有时称为“路径分析”&#xff0c;而后者有时称为“测…

RabbitMQ: 死信队列

一、在客户端创建方式 1.创建死信交换机 2.创建类生产者队列 3.创建死信队列 其实就是一个普通的队列&#xff0c;绑定号私信交换机&#xff0c;不给ttl&#xff0c;给上匹配的路由&#xff0c;等待交换机发送消息。 二、springboot实现创建类生产者队列 1.在消费者里的…

Java后端简历指南(应届)

⭐简单说两句⭐ 作者&#xff1a;后端小知识 CSDN个人主页&#xff1a;后端小知识 &#x1f50e;GZH&#xff1a;后端小知识 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; Java后端简历指南&#xff08;应届&#xff09; 文章目录 Java后端简…