JVM—虚拟机类加载器

news2024/9/23 7:17:59

参考资料:深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明

1. 类加载器

JVM设计团队有意把类加载阶段中的 “通过一个类的全限定名来获取该类的二进制字节流” 这个动作放到JVM外部实现,这个动作的代码称为类加载器。

1.1 类与类加载器

类加载器虽然只作用于实现类的加载动作,但是在Java程序中起到的作用远超类加载阶段。

  • 对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立在其Java虚拟机中的唯一性,也就是比较两个类是否“相等”,只有两个类是同一个类加载器加载的前提下才有意义。

  • 同一个Class文件,被同一个Java虚拟机加载,只要类加载器不同,那么这两个类必然不相等。

这里的“相等”,包括Class对象的equals、isAssignableFrom、isInstance方法的返回结果。也包括了instanceof关键字的对象关系判定。

package JvmTest;

import java.io.IOException;
import java.io.InputStream;

public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {


        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String filename = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(filename);

                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);

                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    throw new ClassNotFoundException(name);
                }
            }
        };

        //loadClass参数代表类的全限定名
        Object obj = myLoader.loadClass("JvmTest.ClassLoaderTest");
        System.out.println(obj.getClass());
        System.out.println(obj instanceof ClassLoaderTest);

    }
}
// 运行结果:
class java.lang.Class
false

1.2 双亲委派模型

从JVM角度来讲只有两种不同的类加载器:一种是启动类加载器,另一个是其他的所有类加载器(都是由Java语言实现,并且全部继承自抽象类ClassLoader

1.2.1 三层类加载器

1.2.1.1 启动类加载器
  • 负责加载<JAVA_HOME>\lib目录的.jar文件

  • 负责加载JRE核心库,这些库是本地代码实现,无法在java中访问到。

  • 启动类加载器无法被java程序直接引用,在自定义类加载器时,将需要加载请求委派给启动类加载器处理。

1.2.1.2 扩展类加载器
  • 负责加载<JAVA_HOME>\lib/ext目录下的所有类库,这些类库提供了对核心类库的扩展。

  • 扩展类加载器由java实现的,因此可以直接在程序中使用扩展类加载器来加载Class文件。

1.2.1.3 应用程序类加载器
  • 应用程序类加载器用于加载应用程序的类和资源(通常从CLASSPATH中指定的目录和JAR文件中加载)。

  • 负责加载用户路径上的所有类库,同样可以直接在代码中使用这个类加载器。

  • 如果没有显示的定义自己的类加载器,那么这个就是应用程序默认的类加载器

1.2.2 双亲委派模型

如图展示的各类加载器之间的层次关系被称为类加载器的“双亲委派模型”,双亲委派模型除了要求有顶层的启动类加载器,并且其余的类都有自己的父类加载器。

不过各类加载器之间不是通过继承关系实现,而是通过组合的关系实现复用。

双亲委派的优点

  • 使用双亲委派模型组织类加载器之间的关系,好处在于Java中的类也享受到了这种优先的层级关系,无论加载哪个类最终都是最顶层启动类加载器加载。

  • 可以保证各种类加载器环境都是同一个类。

  • 如果没有这种机制,例如有一个系统Object和用户编写的Object,会出现重名导致编译通过,但永远无法加载运行

1.2.2.1 双亲委派模型的实现
protected synchronized Class<?> loadClass(String name, boolean resolve) throws
        ClassNotFoundException {
    // 首先,检查请求的类是否已经被加载过了
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            if (parent != null) {
                // 传递给父类
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // 如果父类加载器抛出 ClassNotFoundException
            // 说明父类加载器无法完成加载请求
        }
        if (c == null) {
            // 在父类加载器无法加载时
            // 再调用本身的 findClass 方法来进行类加载
            c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
}

先检查请求加载的类型是否已经被加载过,若没有则调用父加载器的 loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。假如父类加载器加载失败,抛出ClassNotFoundException 异常的话,才调用自己的findClass()方法尝试进行加载。

1.3 破坏双亲委派模型

直到Java模块化的出现之前,双亲委派继承出现过3次较大规模的被“破坏”。

第一次

  • 父类loadClass可能被覆盖

在JDK1.2之前没有双亲委派模型,在此之后为了兼容以前的代码,无法避免loadClass()被子类覆盖的可能,增加了新的方法findClass()来完成加载,引导用户重写这个方法而不是在loadClass中编写代码。

第二次

  • 模型缺陷,双亲委派很好的解决了各类加载器之间的协作时基础类型的一致性问题(越基础的类使用越上层类加载器完成),但是基础类经常被用户继承,调用Api

  • 如果基础类型回调用户代码就会出现问题。

Java设计团队设计了一个线程上下文类加载器,这个类加载器可以通过Thread类的setContextClassLoader()来设置。

它使得父类加载器可以去请求子类加载器完成类加载行为,实际上打破了双亲委派的层次结构。

第三次

  • 由于用户追求程序的动态性导致的,企图实现类似于热插拔的效果,在不重启设备的情况下。

使用OSGi实现模块化热部署,它自定义了类加载机制的实现,每个应用模块(Bundle)都有一个自己的类加载器,当需要更换Bundle时,就连同类加载器一起换掉实现代码的热替换。

但是,OSGi不再使用双亲委派机制,而是发展成一种更加复杂的网状结构。

1.4 Java模块化系统

JDK9引入了模块化系统,为了实现模块化的关键目标—可配置的封装隔离机制。Java对类加载机制也做出了调整。

1.4.1 什么是可配置的封装隔离机制

是一种软件设计模式,它通过将系统中的相关组件(类、函数、变量等)封装到一个独立的容器中,实现组件之间的逻辑隔离。 这个容器可以被动态地配置和修改,实现不同的功能需求。

1.4.2 可配置的封装隔离机制解决的问题

  1. 解决了JDK9之前基于类路径(ClassPath)来查找依赖的可靠性问题。启用了模块化进行封装,模块声明对其他模块的依赖,使得JVM在启动时就能验证应用程序的依赖关系是否完备。

  2. 解决了原来路径上跨JAR文件的public类型的可访问性问题,JDK9的public不意味着程序的所有代码都可以访问,模块提供了更加精细化的访问(必须声明哪些public类型可以被哪些模块访问)。

1.4.3 模块下的类加载器

为了保证兼容性,JDK9没有动摇三层类加载器架构和双亲委派模型,为了保证模块化正常运行,模块化的类加载器发生了一些变化。

  • 首先扩展类加载器平台类加载器取代。

由于整个JDK都基于模块化进行构建,其中Java类库天然满足可扩展的需求,自然不需要扩展类加载器。

  • 平台类加载器和应用程序类加载器继承关系发生变化。

  1. 都不再派生自java.net.URLClassLoader,现在三大加载器全部继承自BuiltinCLassLoader

  2. BuiltinCLassLoader中实现了新模块架构下类如何从模块中加载的逻辑。

  3. 即使现在启动类加载器有了BuiltinCLassLoader这样的java类,但是获取启动类加载器也会返回null代替

1.4.3.1 新的双亲委派机制

  • 当平台以及应用程序类加载器收到类加载请求,在委派给父类之前不会立即委派。

  • 而是先判断类是否可以归属到某个系统模块,如果存在归属关系则优先委派给负责该模块的加载器。

  • 这算是双亲委派机制的第四次破坏。

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

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

相关文章

classical Chinese

classical Chinese 中型娃娃暑假作业背诵 文言文《伯牙鼓琴》 1&#xff09;拿到文言文&#xff0c;先看一遍 2&#xff09;用白话文&#xff08;现代文&#xff09;翻译一次 3&#xff09;用白话文对照回去文言文&#xff08;白话文中那些需要替换回文言文呢&#xff09; 虽…

电脑入门|如何设置默认程序打开文件的软件?弄它!

前言 最近发现一件很奇葩的事情&#xff1a;电脑文件使用不合适的软件打开&#xff0c;然后就以为打不开文件了。 千万不要笑&#xff0c;这个问题是电脑小白经常遇到的。 我曾经见过有小伙伴用Photoshop打开一个.rar的文件…… 奇奇怪怪的事情总会有很多&#xff0c;毕竟谁…

【算法设计题】合并两个非递减有序链表,第1题(C/C++)

目录 第1题 合并两个非递减有序链表 得分点&#xff08;必背&#xff09; 题解 函数声明与初始化变量&#xff1a; 初始化合并链表的头节点&#xff1a; 合并两个链表&#xff1a; 处理剩余节点&#xff1a; 返回合并后的链表&#xff1a; 完整测试代码 &#x1f308;…

如何将文件转换成PDF(四种PDF虚拟打印机介绍)

Microsoft Print To PDF 这是Windows 10及以上系统自带的转换成PDF的工具 运行输入 optionalfeatures 打开可选功能&#xff0c;钩上 [Microsoft Print To PDF] 安装完成后&#xff0c;打开一个支持打印的文件类型或者网页&#xff0c;选择打印&#xff0c;在打印机界面可以看…

4.Redis数据结构通用命令

Redis数据结构 Redis是一个键值对的数据库。 key&#xff1a;大多都是String value: 类型多种多样 Redis通用命令 keys :查看所有的key 不建议在生产环境上使用keys命令&#xff0c;因为redis是单线程的&#xff0c;keys命令会搜索很长一段时间&#xff0c;搜索的期间redi…

[数据集][目标检测]金属罐缺陷检测数据集VOC+YOLO格式8095张4类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;8095 标注数量(xml文件个数)&#xff1a;8095 标注数量(txt文件个数)&#xff1a;8095 标注…

Llama3.1技术报告简要解读--附PDF

以为前些天是在炒作llama3.1泄露&#xff0c;没想到Meta在24号凌晨直接开源了&#xff0c;包括三个不同参数规模的模型&#xff08;8B、70B、405B&#xff09;&#xff0c;三个模型上下文长度都是128K&#xff0c;其中超大杯拥有4050亿参数&#xff0c;从评测指标来看必是最强开…

基于MPC在线优化的有效集法位置控制器simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 模型预测控制是一种基于模型的优化控制方法&#xff0c;它利用过程模型预测未来行为&#xff0c;并通过求解一个优化问题来确定最优控制序列。MPC的特点在于它能够处理系统的动…

罗汉果糖基转移酶 SgUGT94-289-3--文献精读-37

Structural insights into the catalytic selectivity of glycosyltransferase SgUGT94-289-3 towards mogrosides 关于糖基转移酶 SgUGT94-289-3 对罗汉果苷催化选择性的结构洞察 摘要 罗汉果苷是一系列从罗汉果 (Siraitia grosvenorii) 中提取的天然甜味剂。这些罗汉果苷具…

PyQt5入门

Python中经常使用的GUI控件集有PyQt、Tkinter、wxPython、Kivy、PyGUI和Libavg。其中PyQt是Qt(c语言实现的)为Python专门提供的扩展 PyQt是一套Python的GUI开发框架,即图形用户界面开发框架.。而在Python中则使用PyQt这一工具包&#xff08;PyQt5、PyQt5-tools、PyQt5-stubs&am…

数学建模--支持向量机

目录 SVM的基本原理 SVM的应用场景 实现细节与案例分析 总结 支持向量机&#xff08;SVM&#xff09;在处理非线性数据时的核函数有哪些&#xff0c;以及它们各自的优缺点是什么&#xff1f; 如何选择支持向量机的惩罚参数CC以优化模型性能和计算效率&#xff1f; 在实际…

Day17_1--AJAX学习之GET/POST传参

AJAX 简介 AJAX 是一种在无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术。其实AJAX就可以理解为就是JS。通过AJAX也就实现了前后端分离&#xff0c;前端只写页面&#xff0c;后端生成数据&#xff01; 现在开始通过实例学习&#xff1a; 1--GET传参 <!…

4.4 标准正交基和格拉姆-施密特正交化

本节的两个目标就是为什么和怎么做(why and how)。首先是知道为什么正交性很好&#xff1a;因为它们的点积为零&#xff1b; A T A A^TA ATA 是对角矩阵&#xff1b;在求 x ^ \boldsymbol{\hat x} x^ 和 p A x ^ \boldsymbol pA\boldsymbol{\hat x} pAx^ 时也会很简单。第二…

深入解析 kubectl describe pod:全面了解 Kubernetes Pod 的运行状态

引言 在 Kubernetes 集群中&#xff0c;kubectl describe pod 命令是运维人员和开发者常用的工具之一&#xff0c;它提供了有关 Pod 的详细信息&#xff0c;帮助我们了解 Pod 的状态、配置和运行状况。这篇博文将深入解析 kubectl describe pod 命令的输出内容&#xff0c;逐项…

Transformer网络的魔改结构与应用领域

Transformer网络的魔改结构与应用领域 Transformer的基础架构Transformer的变体Transformer的应用领域未来发展方向 参考文献 自从Transformer架构在2017年被提出以来&#xff0c;它已经成为深度学习领域的一项革命性技术。Transformer最初应用于自然语言处理&#xff08;NLP&a…

测试左移,浅谈如何编写可反复执行的单元测试用例

测试左移&#xff0c;浅谈如何编写可反复执行的单元测试用例 背景当下现状期望目标当下困境 解决问题问题1&#xff1a;事务提交Transactional代码示例 问题2&#xff1a;对数据库数据强依赖Sql代码示例SQL脚本示例 问题3&#xff1a;断言assert的使用代码示例DemoTestBaseAppl…

触想工业显示器方案在汽车装配生产线上的应用

一、行业发展背景 中国汽车工业协会数据显示&#xff0c;2023年我国汽车产销量双双实现历史性突破&#xff0c;分别达到3016.1万辆和3009.4万辆&#xff0c;并连续15年位居全球首位。 汽车产业热销背后是先进的生产装配体系支撑&#xff0c;从零部件到整车&#xff0c;汽车的生…

IntelliJ IDEA下载、安装、运行,示例代码;最详细安装和运行教程

IntelliJ IDEA下载、安装、运行&#xff0c;最详细安装教程 以下内容参考&#xff1a; 原文标题&#xff1a;IntelliJ IDEA下载安装教程&#xff08;非常详细&#xff09;从零基础入门到精通&#xff0c;看完这一篇就够了-CSDN博客 原文地址&#xff1a;https://blog.csdn.net/…

汇编语言代码中立即数的前缀和后缀

汇编语言的语法细节非常多&#xff1b; 1 以A-F开头的十六进制数前需要加0 在汇编语言代码中&#xff0c;‌以A-F开头的十六进制数前需要加0以避免被识别为助记符。‌ 如下图&#xff0c; MOV AL, AFH&#xff0c; 这样将报错&#xff1b; AFH的前面加0就对了&#xf…

基于STM32的智能医疗监控系统教程

目录 引言环境准备智能医疗监控系统基础代码实现&#xff1a;实现智能医疗监控系统 生理参数监测模块数据处理与存储模块无线通信模块用户界面与报警系统应用场景&#xff1a;医疗监测与优化常见问题与解决方案收尾与总结 引言 随着健康管理需求的增加&#xff0c;智能医疗监…