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();
        }
    }
    @Override
    protected 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);
    }
    @Override
    protected 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 stats
                   sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                   sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                   sun.misc.PerfCounter.getFindClasses().increment();
               }
           }
           if (resolve) {
               resolveClass(c);
           }
           return c;
       }
   }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Peter算法小课堂—背包问题

我们已经学过好久好久的动态规划了&#xff0c;动态规划_Peter Pan was right的博客-CSDN博客 那么&#xff0c;我用一张图片来概括一下背包问题。 大家有可能比较疑惑&#xff0c;优化决策怎么优化呢&#xff1f;答案是&#xff0c;滚动数组&#xff0c;一个神秘而简单的东西…

AI:125-基于深度学习的航拍图像中地物变化检测

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

uniapp vue3怎么调用uni-popup组件的this.$refs.message.open() ?

vue2代码 <!-- 提示信息弹窗 --><uni-popup ref"message" type"message"><uni-popup-message :type"msgType" :message"messageText" :duration"2000"></uni-popup-message></uni-popup>typ…

【集合系列】TreeMap 集合

TreeMap 集合 1. 概述2. 方法3. 遍历方式4. 排序方式5. 代码示例16. 代码示例27. 代码示例38. 注意事项9. 源码分析 其他集合类 父类 Map 集合类的遍历方式 TreeSet 集合 具体信息请查看 API 帮助文档 1. 概述 TreeMap 是 Java 中的一个集合类&#xff0c;它实现了 SortedMap…

ChatGPT高效提问—prompt常见用法(续篇九)

ChatGPT高效提问—prompt常见用法(续篇九) ​ 如何准确地向大型语言模型提出问题,使其更好地理解我们的意图,从而得到期望的答案呢?编写有效的prompt的技巧,精心设计的prompt,获得期望的的答案。 1.1 增加条件 ​ 在各种prompt技巧中,增加条件是最常用的。在prompt中…

人工智能之大数定理和中心极限定理

大数定律 大数定律&#xff1a;是一种描述当试验次数很大时所呈现的概率性致的定律&#xff0c;由概率统计定义“频率收敛于概率”引申而来。换而言之&#xff0c;就是n个独立分布的随机变量其观察值的均值依概率收敛于这些随机变量所属分布的理论均值&#xff0c;也就是总体均…

精读《js 模块化发展》

1 引言 如今&#xff0c;Javascript 模块化规范非常方便、自然&#xff0c;但这个新规范仅执行了 2 年&#xff0c;就在 4 年前&#xff0c;js 的模块化还停留在运行时支持&#xff0c;10 年前&#xff0c;通过后端模版定义、注释定义模块依赖。对经历过来的人来说&#xff0c;…

区间dp 笔记

区间dp一般是先枚举区间长度&#xff0c;再枚举左端点&#xff0c;再枚举分界点&#xff0c;时间复杂度为 环形石子合并 将 n 堆石子绕圆形操场排放&#xff0c;现要将石子有序地合并成一堆。 规定每次只能选相邻的两堆合并成新的一堆&#xff0c;并将新的一堆的石子数记做该…

qt学习:串口

头文件 #include <QSerialPort> #include <QSerialPortInfo> 模块 QT core gui serialport 编程步骤 配置一个ui界面&#xff0c;五个QComboBox和一个按钮和一个QTextEdit 添加一个成员 private:QSerialPort *serial; 在构造函数中初始化ui端口列表和…

【数据结构和算法】--- 基于c语言排序算法的实现(2)

目录 一、交换排序1.1 冒泡排序1.2 快速排序1.2.1 hoare法1.2.2 挖坑法1.2.3 前后指针法 1.3 快速排序优化1.3.1 三数取中法选key1.3.2 递归到小的子区间使用插入排序 1.4 快排非递归版 二、归并排序2.1 归并排序2.1.1 递归版2.1.2 非递归版 一、交换排序 基本思想&#xff1a…

hexo 博客搭建以及踩雷总结

搭建时的坑 文章置顶 安装一下这个依赖 npm install hexo-generator-topindex --save然后再文章的上面设置 top: number&#xff0c;数字越大&#xff0c;权重越大&#xff0c;也就是越靠顶部 hexo 每次推送 nginx 都访问不到 宝塔自带的 nginx 的 config 里默认的角色是 …

磁盘分区和挂载

一、分区概念 1、基本概念 (1) 一块硬盘最多只能有4个主分区 (2) 其中一个(且最多只能有一个)主分区能作为扩展分区,而扩展分区不能写入数据,只能包含逻辑分区 2、格式化 分区之后的磁盘并不能直接使用&#xff0c;而是需要先进行格式化&#xff0c;又称为逻辑格式化。它是指…

掌握Go的加密技术:crypto/rsa库的高效使用指南

掌握Go的加密技术&#xff1a;crypto/rsa库的高效使用指南 引言crypto/rsa 库概览RSA 加密算法基本原理crypto/rsa 库的功能和应用 安装和基本设置在 Go 项目中引入 crypto/rsa 库基本环境设置和配置 密钥生成与管理生成 RSA 密钥对密钥存储和管理 加密和解密操作使用 RSA 加密…

【HTML+CSS】使用CSS中的Position与z-index轻松实现一个简单的自定义标题栏效果

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起学习和进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&a…

【数据结构与算法】【小白也能学的数据结构与算法】递归 分治 迭代 动态规划 无从下手?一文通!!!

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

【计算机网络】Web HTTP

Web和HTTP HTTP 超文本传输协议 HyperText Transfer Protocol HTTP使用TCP作为支撑传输协议 由一个客户程序和一个服务器程序实现一些常见名词。。。无状态协议 stateless protocol 不保存关于客户的任何信息非持续/持续链接 non-persistent con…

【十三】【C++】vector简单实现

代码实现 /*vector类简单实现*/ #if 1 #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std; #include <vector> #include <algorithm> #include <crtdbg.h> #include <assert.h> #include <string.h>namespace MyVe…

寒假作业2024.2.11

请使用递归实现n! #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <unistd.h> int fun(int n) {if (n0) {return 1;} else {return n*fun(n-1);} } int main(int argc, const char *argv[]) {int n…

嵌入式系统学习指南:从入门到精通

如今嵌入式系统已经广泛应用于工控、消费电子、汽车电子、医疗设备等多个领域。越来越多的IT工程师选择进入嵌入式系统行业。那么作为新手,如何系统地学习嵌入式知识,从入门到精通呢?本文将为大家提供一份简单的自学路线。&#xff08;个人观点&#xff0c;仅供参考&#xff0…

代码随想录 Leetcode55. 跳跃游戏

题目&#xff1a; 代码(首刷自解 2024年2月9日&#xff09;&#xff1a; class Solution { public:bool canJump(vector<int>& nums) {int noz 0;for (int i nums.size() - 2; i > 0; --i) {if (nums[i] 0) {noz;continue;} else {if (nums[i] > noz) noz …