JVM—类加载子系统

JVM—类加载子系统

JVM的类加载是通过ClassLoader及其子类来完成的。

有哪些类加载器

类加载器如下:

类加载器

  • 启动类加载器(BootStrap ClassLoader):负责加载JAVA_HOME\lib目录或通过-Xbootclasspath参数指定路径中的且被虚拟机认可(rt.jar)的类库;
  • 扩展类加载器(Extension ClassLoader):负责加载JAVA_HOME\lib\ext目录或通过java.ext.dirs系统变量指定路径中的类库;
  • 应用程序类加载器(Application ClassLoader):负责加载用户路径classpath上的类库;
  • 自定义类加载器(Custom ClassLoader):加载应用之外的类文件;

类加载器执行顺序

类加载器执行顺序如下图:

类加载器执行顺序

  1. 自底向上检查类是否已经加载:

    加载过程中会先检查类是否已被加载,从自定义加载器到BootStrap逐层检查,只要某个类加载器已加载某个类,就视为此类已加载,可以保证此类使得所有ClassLoader只加载一次;

  2. 自顶向下尝试加载类:由上层来逐层尝试加载此类。

类加载时机与过程

类加载的四个时机:

  1. 遇到new、getStatic、putStatic、invokeStatic四条指令;

    比如有如下类:

    public class MyTest {public static int hello;public static void testMethod(){}
    }
    

    当使用如下三种代码时,此类会被加载:

    //第一种
    MyTest.age;
    //第二种
    MyTest.testMethod();
    //第一种
    new MyTest();
    
  2. 使用java.lang.reflect包方法对类进行反射调用;

    比如:

    Class clazz = Class.forName("com.sjdwz.MyTest");
    
  3. 初始化一个类,发现其父类还没初始化,要先初始化其父类;

  4. 当虚拟机启动时,用户需要指定一个主类main,需要先将主类加载。

一个类的一生

一个类的一生如下:

一个类的一生

类加载做了什么

主要做了三件事:

  1. 根据类全限定名称,定位到class文件,以二进制字节流形式加载到内存中;
  2. 把字节流静态数据加载到方法区(永久代,元空间);
  3. 基于字节流静态数据,创建字节码Class对象。

类加载途径

类加载途径如下图:

类加载途径

自定义类加载器

我们可以自定义类加载器,来加载D:\sjdwzTest目录下的lib文件夹下的类。

步骤如下:

  1. 新建一个类MyTest.java

    package com.sjdwz.myclassloader;
    public class MyTest {public void sayHello(){System.out.println("hello world!");}
    }
    
  2. 使用javac MyTest.java命令,将生成的MyTest.class文件放到D:\sjdwzTest\lib\com\sjdwz\myclassloader文件夹下

    注意:包路径不能错。

    编译的位置

  3. 自定义类加载器,继承ClassLoader,重写findClass()方法 ,调用defineClass()方法:

    /*** @Description 自定义类加载器* @Created by 随机的未知*/
    public class SjdwzClassLoader extends ClassLoader {private String classpath;public SjdwzClassLoader(String classpath) {this.classpath = classpath;}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {try {//输入流,通过类的全限定名称加载文件到字节数组byte[] classDate = getData(name);if (classDate != null) {//defineClass方法将字节数组数据 转为 字节码对象return defineClass(name, classDate, 0, classDate.length);}} catch (IOException e) {e.printStackTrace();}return super.findClass(name);}/*** 加载类的字节码数据* @param className* @return* @throws IOException*/private byte[] getData(String className) throws IOException{String path = classpath + File.separatorChar +className.replace('.', File.separatorChar) + ".class";try (InputStream in = new FileInputStream(path);ByteArrayOutputStream out = new ByteArrayOutputStream()) {byte[] buffer = new byte[2048];int len = 0;while ((len = in.read(buffer)) != -1) {out.write(buffer, 0, len);}return out.toByteArray();} catch (FileNotFoundException e) {e.printStackTrace();}return null;}
    }
    
  4. 测试类如下:

    public class SjdwzClassLoaderTest {public static void main(String[] args) throws Exception {//自定义类加载器的记载路径SjdwzClassLoader sjdwzClassLoader = new SjdwzClassLoader("D:\\sjdwzTest\\lib");Class<?> testClazz = sjdwzClassLoader.loadClass("com.sjdwz.myclassloader.MyTest");if(testClazz != null){Object testObj = testClazz.newInstance();Method sayHelloMethod = testClazz.getMethod("sayHello", null);sayHelloMethod.invoke(testObj,null);System.out.println(testClazz.getClassLoader().toString());}}
    }
    

    输出如下:

    输出

双亲委派与打破双亲委派

什么是双亲委派

当一个类加载器收到类加载任务,会先交给其父类加载器去完成。 因此,最终加载任务都会传递到顶层的启动类加载器,只有当父类加载器无法完成加载任务时,子类才会尝试加载任务。

为什么需要双亲委派

主要考虑安全因素,双亲委派可以避免重复加载核心的类,当父类加载器已经加载了该类时,子类加载器不会再去加载。

为什么还需要破坏双亲委派

在实际应用中,双亲委派解决了Java基础类统一加载的问题,但是存在着缺陷。JDK中的基础类的方法作为典型的API被用户类用户调用,但是也存在API调用用户代码的情况,比如:SPI代码。这种情况就需要打破双亲委派模式。

比如:数据库驱动DriverManager。以Driver接口为例,Driver接口定义在JDK中,其实现由各个数据库的服务商来提供,由系统类加载器加载。这个时候就需要启动类加载器来委托子类来加载Driver实现,这就破坏了双亲委派。

如何破坏双亲委派

  1. 重写ClassLoader的loadClass方法;

    在JDK1.2之后,新加了一个findClass方法让用户重写;

  2. SPI,父类委托子类加载器加载Class;

  3. 热部署和不停机更新用到的OSGI技术。

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

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

相关文章

FaaF:利用事实作为评估RAG的函数方法

原文地址&#xff1a;faaf-facts-as-a-function-for-evaluating-rag 2024 年 4 月 5 日 在某些情况下&#xff0c;我们使用其他语言模型来验证RAG的输出结果&#xff0c;但这种方法并未能有效识别出数据生成过程中的错误和缺失。 论文解析 挑战 评估的可靠性和效率&#xff…

练习题(2024/4/6)

1最接近的三数之和 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数&#xff0c;使它们的和与 target 最接近。 返回这三个数的和。 假定每组输入只存在恰好一个解。 示例 1&#xff1a; 输入&#xff1a;nums [-1,2,1,-4], target …

并查集python实现及题目练习

文章目录 1. 并查集概念1.1 理解并查集&#xff1a;简介与应用场景1.2 Python 实现并查集及优化策略1.3 扁平化栈实现1.4 分析并查集的时间复杂度 2. 情侣牵手3. 相似字符串4. 岛屿数量 如果想了解并查集基础推荐去看左程云大神的算法讲解&#xff0c;非常不错&#xff0c;b站和…

python 02字符串

字符串可能是用到最多的数据类型了&#xff0c;所有标准序列操作&#xff08;索引、切片、乘法、成员资格检查、长度、最小值和最大值&#xff09;都适用于字符串 但别忘了字符串是不可变的&#xff0c;因此所有的元素赋值和切片赋值都是非法的。 1.居中效果 默认为空格 可…

在不同操作系统中搭建Python编程环境

1 在不同操作系统中搭建Python编程环境 1.1 在Linux系统中搭建Python编程环境 1. 检查Python版本 在你的系统中运行应用程序Terminal&#xff08;如果你使用的是Ubuntu&#xff0c;可按Ctrl Alt T&#xff09;&#xff0c;打开一个终端窗口。为确定是否安装了Python&…

为何网易游戏会选择引入OceanBase数据库

本文作者&#xff1a;田维繁&#xff0c;网易游戏关系型数据库小组负责人 作为中国游戏开发领域的佼佼者&#xff0c;网易游戏始终站在网络游戏自主研发的前沿。其产品及周边产品线丰富多样&#xff0c;因此&#xff0c;为满足各种业务场景的需求&#xff0c;需要多种不同的数据…

【环境变量】常见的环境变量 | 相关指令 | 环境变量系统程序的结合理解

目录 常见的环境变量 HOME PWD SHELL HISTSIZE 环境变量相关的指令 echo&env export unset 本地变量 环境变量整体理解 程序现象_代码查看环境变量 整体理解 环境变量表 环境变量表的传递 环境变量表的查看 测试验证 少说废话&#x1f197; 每个用户…

48V转15V,48V转12V,48V转24V高效率降压恒压芯片SL3041电路简单

在现代电子设备中&#xff0c;电源转换是一个关键且常见的技术。特别是对于那些需要将48V电压转换为更低电压&#xff08;如15V、12V或24V&#xff09;的设备&#xff0c;一个高效、可靠的降压恒压芯片至关重要。本文将详细介绍一款名为SL3041的高效率降压恒压芯片&#xff0c;…

#{} 和 ${}区别

1、参数是Integer类型时候没区别&#xff08;#是预编译SQL&#xff0c;$是即时SQL&#xff09; 2、当参数是String类型时&#xff0c;就会出错了 &#xff08;1&#xff09;这是$的报错信息&#xff0c;因为我们的参数admin并没有加引号所以不满足字符串条件 (2)正确的SQL &am…

蓝牙学习十(扫描)

一、简介 从之前的文章中我们知道&#xff0c;蓝牙GAP层定义了四种角色&#xff0c;广播者&#xff08;Broadcaster&#xff09;、观察者&#xff08;Observer&#xff09;、外围设备&#xff08;Peripheral&#xff09;、中央设备&#xff08;Central&#xff09;。 之前的学习…

基础总结篇:Activity生命周期

private int param 1; //Activity创建时被调用 Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, “onCreate called.”); setContentView(R.layout.lifecycle); Button btn (Button) findViewById(R.id.…

Python实现 AI 绘图(非常详细)零基础入门到精通,收藏这一篇就够了

今天给大家带来了 Python 对接阿里大模型&#xff0c;通过 AI 实现文本生成图片。 相关资料 这个功能使用的主要 API 是阿里的大模型服务。 开通服务 阿里文档 https://help.aliyun.com/zh/dashscope/developer-reference/quick-start-1?disableWebsiteRedirecttrue 获取 a…

K8S之Job和CronJob控制器

这里写目录标题 Job概念适用场景使用案例 CronJob概念适用场景使用案例 Job 概念 Job控制器用于管理Pod对象运行一次性任务&#xff0c;例如&#xff1a;对数据库备份&#xff0c;可以直接在k8s上启动一个mysqldump备份程序&#xff0c;也可以启动一个pod&#xff0c;这个pod…

Day3-HBase重要概念

HBase 结构 HRegion 概述 在HBase中&#xff0c;会从行键方向上对表来进行切分&#xff0c;切分出来的每一个结构称之为是一个HRegion 切分之后&#xff0c;每一个HRegion会交给某一个HRegionServer来进行管理。HRegionServer是HBase的从节点&#xff0c;每一个HRegionServ…

C++ 内存分配时地址对齐

如果数据地址的对齐与CPU相兼容&#xff0c;那么CPU读写内存时性能会更高。 因此在C中&#xff0c;有时会希望在堆或栈中分配内存时&#xff0c;返回的地址能按照特定的长度对齐。 如果希望在栈中分配的内存时&#xff0c;返回地址按照特定长度对齐&#xff0c;可以使用 alig…

光伏接口转接器配合光伏规约转换器实现发电用电信息采集支持接入各个型号逆变器

1.产品概述 DAQ-GP-485PIA光伏接口转接器&#xff08;以下简称转接器&#xff09;是我公司针对光伏发电领域国家电网公司最新需求设计的&#xff0c;光伏接口转接器是配合光伏规约转换器&#xff0c;实现逆变器发电、用电信息采集的设备。支持锦浪、古瑞瓦特、固德威、华为、奥…

提升团队工程交付能力,从“看见”工程活动和研发模式开始

作者&#xff1a;张裕、雅纯 理想中的研发团队应当具有以下特征&#xff1a; 总是工作在最高优先级的事项上 理想的研发团队能够识别并始终集中精力在当前最紧迫和最有价值的任务上。这需要团队具备出色的项目管理能力和决策能力&#xff0c;以便能够正确评估优先级&#xff0…

Vue 项目如何在VSCode中进行断点调试

概要&#xff1a; 简单介绍一下&#xff0c;在VSCode中如何对Vue项目进行断点调试。具体内容包括但不限于&#xff1a;如何配置调试环境、如何在代码中设置断点、如何启动调试以及如何解决在调试过程中可能遇到的问题。 实际开发中&#xff0c;我们的前端项目一般都是在浏览器…

算法 day28 回溯4

93 复原IP地址 给定一个只包含数字的字符串 s &#xff0c;用以表示一个 IP 地址&#xff0c;返回所有可能的有效 IP 地址&#xff0c;这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。 有效 IP 地址 正好由…

windows 系统下 mysql 数据库的下载与安装(包括升级安装)

windows 系统下 mysql 数据库的下载与安装&#xff08;包括升级安装&#xff09; 一、mysql 介绍&#xff1a; MySQL 是一个关系型数据库管理系统&#xff0c;由瑞典 MySQL AB 公司开发&#xff0c;属于 Oracle 旗下产品。 MySQL 是最流行的关系型数据库管理系统之一&#xf…