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

news2024/10/5 21:22:08

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

什么是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;
       }
   }

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

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

相关文章

centos安装harbor

安装docker yum install epel-release yum-utils#docker官网源 yum-config-manager \--add-repo \https://download.docker.com/linux/centos/docker-ce.repoyum install docker-ce -y 安装docker-compose curl -SL https://github.com/docker/compose/releases/download/v2…

springBoot,springSecurity返回乱码

框架&#xff1a;SpringBoot3 问题&#xff1a;响应内容乱码 问题代码&#xff1a; // 成功登录响应的内容Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication…

C++笔记之regex(正则表达式)

C++笔记之regex(正则表达式) ——2024-02-10 ——《C++标准库》(第2版,侯捷译) Page 717 code review! 文章目录 C++笔记之regex(正则表达式)例1:使用正则表达式进行搜索(`std::regex_search`)例2:使用正则表达式进行全文匹配(`std::regex_match`)例3:使用正则表达式…

【大厂AI课学习笔记】1.5 AI技术领域(1)计算机视觉

人工智能的三大基础应用领域是&#xff0c;自然语言处理&#xff0c;语音识别&#xff0c;计算机视觉。 计算机视觉&#xff1a;定义、关键技术、技术发展、应用场景与商业化成功 一、计算机视觉的定义 计算机视觉&#xff0c;作为一个跨学科的领域&#xff0c;旨在研究如何让…

学习Android的第十天

目录 Android CheckBox 复选框 获得选中的 CheckBox 的值 自定义点击效果 改变文字与选择框的相对位置 修改文字与选择框的距离 Android ToggleButton 开关按钮 改变 ToggleButton 的状态和文本 Android Switch 开关 改变 Switch 的状态和文本 Android CheckBox 复选框…

IDEA中Git的使用小技巧-Toolbar(工具栏)的设置

目录 1 前言 2 步骤 2.1 打开设置 2.2 找到Menus and Toolbars 2.3 Menus and Toolbars界面的介绍 2.4 选择工具 2.5 查看 1 前言 工具栏的合理运用&#xff0c;能够极大程度上为我们省时省力 &#xff0c;接下来我将以Git工具的添加&#xff0c;介绍如何定制我们IDEA…

【新书推荐】7.3 for语句

本节必须掌握的知识点&#xff1a; 示例二十四 代码分析 汇编解析 for循环嵌套语句 示例二十五 7.3.1 示例二十四 ■for语句语法形式&#xff1a; for(表达式1;表达式2;表达式3) { 语句块; } ●语法解析&#xff1a; 第一步&#xff1a;执行表达式1&#xff0c;表达式1…

【Linux】学习-基础IO—上

Linux基础IO—上 复习c语言接口 你真的懂文件吗&#xff1f; 文件的打开与关闭 深入了解文件读与写(C语言级别) 系统文件I/O 我们知道&#xff0c;文件是放在磁盘(硬件)上的&#xff0c;我们用代码访问文件的思路是&#xff1a; 写代码 -> 编译 -> 生成可执行exe …

uniapp小程序端使用计算属性动态绑定style样式踩坑

踩坑点: 使用uniapp编译小程序端动态绑定复杂style使用计算属性方式&#xff0c;return必须返回json字符串格式&#xff0c;不能返回object&#xff0c;否则会不起作用。 代码总览 视图层 逻辑层&#xff08;注意这里是使用的计算属性哈&#xff09; 这里我封装成了一个个性化…

泽攸科技ZEM系列台扫助力环境科研创新:可见光催化抗生素降解的探索

环境污染和能源短缺是当今人类社会面临的最严重威胁之一。为了克服这些问题&#xff0c;特别是在污水处理过程中&#xff0c;寻找新的技术来实现清洁、高效、经济的发展显得尤为重要。在各种工业废水中&#xff0c;抗生素的过量排放引起了广泛关注。抗生素的残留会污染土壤、水…

学会这几个Dynamo数据处理技巧,效率翻倍提升

最近一直在写Dynamo程序&#xff0c;但是很多东西是不能分享出来的&#xff0c;但是一些方法是可以分享的&#xff0c;整理了一些&#xff0c;今天先分享几个给大家。话不多说&#xff0c;直接进入主题&#xff1a; 1、快速筛选出一组物体中&#xff0c;有哪些物体与另一组物体…

【Maven】依赖、构建管理 继承与聚合 快速学习(3.6.3 )

文章目录 Maven是什么&#xff1f;一、Maven安装和配置本地配置文件设置idea配置本地maven 二、基于IDEA的Maven工程创建2.1 Maven工程GAVP属性2.2 Idea构建Maven JavaEE工程 三、Maven工程项目结构说明四、Maven核心功能依赖和构建管理4.1 依赖管理和配置4.2 依赖传递和冲突4.…

C# OCR识别图片中的文字

1、从NuGet里面安装Spire.OCR 2、安装之后&#xff0c;找到安装路径下&#xff0c;默认生成的packages文件夹&#xff0c;复制该文件夹路径下的 6 个dll文件到程序的根目录 3、调用读取方法 OcrScanner scanner new OcrScanner(); string path "C:\1.png"; scann…

编译环境搭建及基础实验

1.VS code安装 Linux 版本安装 把资料盘里的安装包.deb拷贝到Ubuntu中&#xff0c; 使用如下命令安装&#xff1a; 软件图标都在目录/usr/share/applications 中&#xff0c;如图路径 复制到桌面中 Visual Studio Code 插件的安装 我们需要按照的插件有下面几个&#xff1a;…

[C#]winform制作圆形进度条好用的圆环圆形进度条控件和使用方法

【创建圆形进度条流程】 在C# WinForms应用程序中创建一个圆形进度条&#xff08;通常用作仪表盘的显示&#xff09;可以通过多种方式实现。下面是一个简单的例子&#xff0c;演示如何使用System.Drawing命名空间中的图形绘制功能来绘制一个基本的圆形进度条。 首先&#xff0…

随机MM引流源码PHP开源版

引流源码最新随机MM开源版PHP源码&#xff0c;非常简洁好看的单页全解代码没任何加密 直接上传即可用无需数据库支持主机空间

linux应用 进程间通信之信号量(System V)

1、定义 System V 信号量是一种用于进程间同步和互斥的机制&#xff0c;它是 System V IPC&#xff08;Inter-Process Communication&#xff0c;进程间通信&#xff09;机制的一部分。信号量通常用于控制对共享资源的访问&#xff0c;以避免竞争条件&#xff08;race conditi…

【EAI 017】Interactive Language: Talking to Robots in Real Time

论文标题&#xff1a;Interactive Language: Talking to Robots in Real Time 论文作者&#xff1a;Corey Lynch, Ayzaan Wahid, Jonathan Tompson Tianli Ding, James Betker, Robert Baruch, Travis Armstrong, Pete Florence 作者单位&#xff1a;Robotics at Google 论文原…

口腔助手|口腔挂号预约小程序|基于微信小程序的口腔门诊预约系统的设计与实现(源码+数据库+文档)

口腔小程序目录 目录 基于微信小程序的口腔门诊预约系统的设计与实现 一、前言 二、系统功能设计 三、系统实现 1、小程序前台界面实现 2、后台管理员模块实现 四、数据库设计 1、实体ER图 2、具体的表设计如下所示&#xff1a; 五、核心代码 六、论文参考 七、最新…

【软件设计师】——面向对象设计原则

为了提高软件的可维护性、可复用性&#xff0c;增加软件的可扩展性和灵活性&#xff0c;在面向对象编程的过程中我们需要遵守以下六条原则。 开闭原则 定义&#xff1a; 编写的代码需要对 扩展开放 对 修改关闭 &#xff0c;实现 热插拔 的效果。 例&#xff1a;在编写不同皮…