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

JVM-类加载器 双亲委派机制

申明:文章内容是本人学习极客时间课程所写,文字和图片基本来源于课程资料,在某些地方会插入一点自己的理解,未用于商业用途,侵删。

什么是JVM

JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。由一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域等组成。JVM屏蔽了与操作系统平台相关的信息,使得Java程序只需要生成在Java虚拟机上运行的目标代码(字节码),就可在多种平台上不加修改的运行,这也是Java能够“一次编译,到处运行的”原因。

在这里插入图片描述

类加载

类加载器的定义(深入理解JVM原话):
通过一个类的全限定名称来描述此类的二进制字节流,将这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定获取所需要的类,实现这个动作的代码模快称为类加载器。
1 JVM 的类加载是通过ClassLoader及子类来完成的,通常来说有下面几种类加载器:

  • 启动类加载器(Bootstrap ClassLoader)
    负责加载JAVA_ HOME\lib目录的或通过-Xbootclasspath参数指定路径中的且被虚拟机认可(rt.jar) 的类库。由C++实现,不是ClassLoader的子类
  • 扩展类加载器(Extension ClassLoader)
    负责加载JAVA_ _HOME\lib\ext目录或通过java.ext.dirs系统变量指定路径中的类库
  • 应用程序类加载器(Application ClassLoader)
    负责加载用户路径classpath上的类库
  • 自定义类加载器
    JVM 只能加载放在指定路径下的字节码,某些时候我们需要加载自己的class文件就需要用到自定义类加载器。

2 类加载执行顺序
检查顺序是自底向上:加载过程中会先检查类是否被已加载,从Custom到BootStrap逐层检查,只要某个类加载器已加载就视为此类已加载,保证此类所有ClassLoader只加载一 次.
在这里插入图片描述
3 加载时机(检查时自底向上,加载时自顶向下)

1-遇到new、getStatic、 putStatic、 invokeStatic四条指令时。
2-使用java.lang.reflect包方法时,对类进行反射调用。
3-初始化这个类时,发现其父类还没初始化,要先初始化其父类。
4-当虚拟机启动时,用户需要指定–个主类Main,需要先将主类加载。

4 一个类的一生
在这里插入图片描述

5 类加载所做的事情
在类加载的过程中,做了如下几件事情:
1 根据全限定名称加载二进制字节流。
2 将字节流转换为数据结构
3 创建字节码class的对象

6 类加载途径
➢01-jar/war
➢02-jsp生成的class
➢03-数据库中的二进制字节流
➢04-网络中的二进制字节流
➢05-动态代理生成的二进制字节流

在这里插入图片描述
自定义类加载器案例helloworld

public class CustomClassLoader extends ClassLoader {private final String classPath;public CustomClassLoader(String classPath) {this.classPath = classPath;}public static void main(String[] args) {CustomClassLoader customClassLoader = new CustomClassLoader("E:\\lesson-one\\lesson-one\\src\\lib");try {Class<?> c = customClassLoader.loadClass("com.learn.lessonone.dto.Test");if (c != null) {Object o = c.newInstance();Method say = c.getMethod("say", null);say.invoke(o, null);System.out.println(c.getClassLoader().toString());}} catch (Exception e) {e.printStackTrace();}}@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {try {byte[] calsssDate = getData(name);if (calsssDate != null) {return defineClass(name, calsssDate, 0, calsssDate.length);}} catch (Exception e) {e.printStackTrace();}return super.loadClass(name, resolve);}@Overrideprotected Object getClassLoadingLock(String className) {return super.getClassLoadingLock(className);}public byte[] getData(String className) {String path = classPath + File.separator + className.replace(".", File.separator) + ".class";try (InputStream in = new FileInputStream(path);ByteArrayOutputStream out = new ByteArrayOutputStream()) {byte[] buffer = new byte[1024];int len = 0;while ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);}} catch (Exception e) {e.printStackTrace();}return null;}
}
类加载机制双亲委派

1-什么是双亲委派?
当一个类加载器收到类加载任务,会先交给其父类加载器去完成,因此最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,才会尝试执行加载任务。

2-为什么需要双亲委派呢?
2-1 双亲委派其实是一种规范,它一定程度上能够保证安全性。就比如我们尝试用的Object,String类,如果我们没有委托父类进行加载,每个子类进行加载,如果这个时候我们自己写了一个类的全限定名称和系统的一模一样,这个时候它加载的就是我们写的类,这样就会导致我们使用的不是Java给我门头提供的Object类,从而程序完全乱套。
为什么双亲委派能够解决这个问题呢,因为我们会一直委托父类去加载,加载Object这种类最终都是由BootstrapClassLoader来加载,它保证了加载的一定是Java提供给我们的Object类,因为BootstrapClassLoader就是负责加载这类内置类的(也就是加载java 固定路径下的一些类)。
2-2 双亲委派可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
3-为什么还需要破坏双亲委派?
在实际应用中,双亲委派解决了Java 基础类统一加载的问题,但是却存在着缺陷。JDK中的基础类作为典型的API被用户调用,但是也存在API调用用户代码的情况,JNDI,SPI,这种情况就需要打破双亲委派模式。
例如:数据库驱动DriverManager。以Driver接口为例,Driver接口定义在]DK中,其实现由各个数据库的服务商来提供,由系统类加载器加载。这个时候就需要启动类加载器来委托子类来加载Driver实现,这就破坏了双亲委派。从下面这段源码来看,我们加载类加载DriverManager是由bootstracpClassLoader加载的,但是我们加载不同厂商的Driver是拿的线程的自定义类加载器去加载的。
在这里插入图片描述

4-如何破坏双亲委派?
方式一:重写ClassLoader的loadClass方法
方式二:SPl,类委托自类加载器加载Class,以数据库驱动DriverManager为例
方式三:为了满足热部署、不停机更新需求。OSGI 就是利用自定义的类加载器机制来完成模块化热部署,而它实
现的类加载机制就没有完全遵循自下而上的委托,有很多平级之间的类加载器查找。
自己的理解:
自定义的类始最终都是由ApplicationClassLoader或自定义类加载器加载比如我写了一个CustomObject 继承了Object这个类 并定义了这个类字段结构。在执行加载的时候,CustomObject一直向上委托,最后发现BootStrapClassLoaer加载不了,然后又自顶向下回溯ApplicationClassLoader来加载CustomObject这个类,但是Object BootStrapClassLoaer是能加载的,在回到这里之前Object已将被加载过了,因为它会被BootStrapClassLoader加载。
类加载的源码 可以类的加载时委托自己的父亲进行加载

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded// 首先检查类是否已被加载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();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;}}
http://www.lryc.cn/news/300435.html

相关文章:

  • vue axios 请求后端无法传参问题
  • 打印最小公倍数
  • [AIGC] Java 和 Kotlin 的区别
  • 蓝桥杯电子类单片机提升一——超声波测距
  • 前端架构: 脚手架开发流程中的难点梳理
  • django中配置使用websocket
  • Rust复合类型详解
  • 学习 JavaScript 闭包
  • VScode中配置 C/C++ 环境 | IT拯救者
  • 基于Python实现Midjourney集成到(个人/公司)平台中
  • 蓝桥杯刷题--python-6
  • node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查
  • 【Android】使用Apktool反编译Apk文件
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • Django模板(二)
  • 勒索病毒最新变种.faust勒索病毒来袭,如何恢复受感染的数据?
  • python 人脸检测器
  • 机器学习与深度学习
  • 算法训练营day27(补),贪心算法1
  • [office] excel2003限定单元格输入值范围教程 #微信#经验分享
  • OLED显示红外遥控键码
  • LabVIEW智能温度监控系统
  • 专业140+总分420+浙江大学842信号系统与数字电路考研经验电子信息与通信,真题,大纲,参考书。
  • C语言学习day15:数组强化训练
  • 缓存穿透、缓存击穿与缓存雪崩
  • 一周学会Django5 Python Web开发-项目配置settings.py文件-模版配置
  • CF1845 D. Rating System [思维题+数形结合]
  • HeidiSQL安装配置(基于小皮面板(phpstudy))连接MySQL
  • 【蓝桥2013】错误票据
  • nvm对node版本进行管理及疑难解决,vue项目搭建与启动