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,一经查实,立即删除!

相关文章

【leetcode面试经典150题】2.移除元素(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

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

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

4月底,这门HCIA认证即将发布!

非常荣幸地通知您&#xff0c;华为认证HCIA-AI Solution V1.0&#xff08;中文版&#xff09;预计将于2024年4月30日正式对外发布。 为了帮助您做好学习、培训和考试计划&#xff0c;现进行预发布通知&#xff0c;请您关注。 01 发布概述 基于“平台生态”战略&#xff0c;围绕…

练习题(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&…

能源党建后台项目总结

1.引入 本次框架是Ruoyi-plusvue2element组合。 2.样式 由于是后台项目&#xff0c;样式要求统一&#xff0c;不可以有的输入框长有的短。着重几点&#xff1a; 1.关于form表单应该如何水平布局 在element中&#xff0c;form有个属性叫&#xff1a;:inline"true"…

C++ Algorithm 常用算法

C <algorithm> 头文件是标准库中提供的一系列算法&#xff0c;用于操作范围&#xff08;range&#xff09;内的元素。这些算法可以用于数组、容器如vector和list&#xff0c;以及其他满足相应迭代器要求的数据结构。以下是一些常用的C <algorithm> 中的算法及其使用…

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

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

算法| ss 双指针

11.盛水最多的容器15.三数之和26.删除有序数组中的重复项27.移除元素75.颜色分类88.合并两个有序数组167.两数之和2-输入有序数组581.最短无序连续子数组2486.追加字符以获得子序列 11.盛水最多的容器 /*** param {number[]} height* return {number}*/ // 思路 // 左0 右 n-…

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

目录 常见的环境变量 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;…

C++ P1152 欢乐的跳

文章目录 一、题目描述欢乐的跳题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 样例 #2样例输入 #2样例输出 #2 提示 二、参考代码 一、题目描述 欢乐的跳 题目描述 一个 n n n 个元素的整数数组&#xff0c;如果数组两个连续元素之间差的绝对值包括了 [ 1 , n −…

#{} 和 ${}区别

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.…

在JS中通过样式构建三角形的方法

第一种方法&#xff1a; .triangle {width: 0;height: 0;border-left: 50px solid transparent;border-right: 50px solid transparent;border-bottom: 100px solid blue; /* 三角形的颜色 */ }.triangle 类定义了一个零宽高的块级元素&#xff0c;并通过设置边框的样式来创建…

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…