【JVM】类加载器

news2024/11/20 2:43:09

类与类加载器

类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段。对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每 一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个 Class文件,被同一个Java虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。

image-20230922123211002

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);
                }
            }
        };
        Object obj = myLoader.loadClass("com.ttpfx.classloader.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof com.ttpfx.classloader.ClassLoaderTest);
    }
}

上面代码输出结果为

class com.ttpfx.classloader.ClassLoaderTest
false

输出false的原因是Java虚拟机中同时存在了两个ClassLoaderTest类,一个是由虚拟机的应用程序类加载器所加载的,另外一个是由我们自定义的类加载器加载的,虽然它们都来自同一 个Class文件,但在Java虚拟机中仍然是两个互相独立的类,做对象所属类型检查时的结果自然为false

双亲委派模型

站在Java虚拟机的角度来看,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另外一种就是其他所有的类加载器,这些类加载器都由Java语言实现,独立存在于虚拟机外部,并且全都继承自抽象类 java.lang.ClassLoader。

JDK 1.2以来,Java一直保 持着三层类加载器、双亲委派的类加载架构

在Jdk8中,绝大多数Java程序都会使用到以下3个系统提供的类加载器来进行加载

  • 启动类加载器(Bootstrap Class Loader):这个类加载器负责加载存放在 \lib目录,或者被-Xbootclasspath参数所指定的路径中存放的类
  • 扩展类加载器(Extension Class Loader):这个类加载器是在类sun.misc.Launcher$ExtClassLoader 中以Java代码的形式实现的。它负责加载\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库
  • 应用程序类加载器(Application Class Loader):也称它为“系统类加载器“,它负责加载用户类路径 (ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

JDK 9之前的Java应用都是由这三种类加载器互相配合来完成加载的,如果用户认为有必要,还可 以加入自定义的类加载器来进行拓展,典型的如增加除了磁盘位置之外的Class文件来源,或者通过类 加载器实现类的隔离、重载等功能。

类加载器之间的协作关系如下

image-20230922124827151

各种类加载器之间的层次关系被称为类加载器的“双亲委派模型(Parents Delegation Model)”。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。不过这里类加载器之间的父子关系一般不是以继承(Inheritance)的关系来实现的,而是通常使用 组合(Composition)关系来复用父加载器的代码。

双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请 求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。、

使用双亲委派机制的好处就是确保了类的唯一性和安全性,避免了类的重复加载和不安全的类替代。

loadClass方法源码如下

    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;
        }
    }

可以很清晰的看见类加载器在加载类之前都会先调用父类类加载器加载,如果加载失败才会调用当前类加载器

破坏双亲委派机制

当我们说要破坏双亲委派模型时,我们指的是在类加载机制中有意修改默认的双亲委派规则,以便更灵活地加载类或实现一些特殊的需求。下面详细解释如何实现这种破坏以及何时使用它:

  1. 自定义类加载器

    自定义类加载器是破坏双亲委派模型的最常见方式之一。您可以继承java.lang.ClassLoader类并覆盖loadClass方法来实现自定义的加载逻辑。在这个方法中,您可以指定在加载类时要采取的策略,例如是否使用双亲委派机制。

    public class CustomClassLoader extends ClassLoader {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            // 自定义加载逻辑,不按照双亲委派模型加载类
            return super.loadClass(name);
        }
    }
    

    使用自定义类加载器,可以加载特定位置的类文件、实现热替换或根据特定条件来加载类,而不受默认的双亲委派机制的限制。

  2. 模块化系统

    自从Java 9引入模块化系统,模块之间的依赖关系和可见性规则变得更加灵活。通过在模块描述文件中定义exports关键字,您可以指定哪些包对其他模块可见,从而允许某些类可以直接访问其他模块的类,而不受双亲委派机制的限制。

    module com.example.mymodule {
        // 将包com.example.othermodule中的类对外导出
        exports com.example.othermodule;
    }
    

    这种方式允许您明确控制模块之间的类的可见性,而不必依赖于默认的双亲委派模型。

  3. 热替换

    热替换是一种在应用程序运行时替换已加载的类的技术,通常在开发和调试过程中使用。它可以使用Java的Instrumentation API或自定义类加载器来实现。通过重新加载类,您可以在不重启应用程序的情况下进行代码修改和调试,这超越了双亲委派模型的限制。

总之,破坏双亲委派模型通常用于实现一些高级的类加载需求,例如定制的类加载逻辑、模块化系统的配置、热替换等。但是,需要注意的是,破坏双亲委派模型可能会引入潜在的安全和一致性问题,因此在使用这些方法时需要慎重考虑,并确保安全性和可维护性。

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

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

相关文章

k8s 中master 与node的通信

一、Master 节点与 Node 节点通讯原理 Master 节点启动时&#xff0c;会运行一个 kube-apiserver 进程&#xff0c;它提供了集群管理的 API 接口&#xff0c;是集群内各个功能模块之间数据交互和通信的中心枢纽&#xff0c;并且它页提供了完备的集群安全机制。在 Node 节点上&a…

【新品发布】洛微科技全新工业级高性能 D系列 TOF相机D3重磅上线!

近日&#xff0c;洛微科技对外发布新款高性能D系列 TOF相机D3&#xff0c;这是一款专为工业环境中高性能操作设计的3D TOF智能相机。 D3基于行业领先的Sony DepthSense像素技术开发&#xff0c;具有毫米级测量精度、VGA深度分辨率、抗环境光能力强、软/硬件多触发方式、HDR适配…

夸克扫描王识别精度领跑行业 愿携手各方伙伴探索AIGC应用新范式

日前&#xff0c;在“AIGC与场景化应用创新”主题研讨会上&#xff0c;阿里智能信息事业群夸克视觉技术负责人黄锐华分享了AI技术应用于扫描场景的最新成果。他表示&#xff0c;AIGC给手机扫描产品打开了创新空间&#xff0c;搭载大模型技术的夸克扫描王对识别手写字体、复杂公…

【验证码逆向专栏】螺丝帽人机验证逆向分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未…

Maven 查看项目中的依赖

文章目录 通过 Maven 命令查看依赖dependency:listdependency:tree将命令结果输出到文档 视图化查看依赖Maven HelperIDEA 自带工具 想查看项目中使用的依赖列表时&#xff0c;可以参考下面几种方式 通过 Maven 命令查看依赖 在有 pom.xml 的路径下&#xff0c;通过命令行工…

人工智能(pytorch)搭建模型19-手把手利用pytorch框架搭建目标检测DarkNet模型,并展现网络结构

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能(pytorch)搭建模型19-手把手利用pytorch框架搭建目标检测DarkNet模型&#xff0c;并展现网络结构。随着深度学习技术的不断发展&#xff0c;各种卷积神经网络模型层出不穷&#xff0c;其中DarkNet作为一种快…

CMD脚本实战教程

要在 Windows 11 上编写一个自定义关机的 CMD 脚本文件&#xff0c;你可以创建一个扩展名为 .bat 或 .cmd 的文本文件&#xff0c;并在其中编写脚本。 一、常用语法 rem&#xff1a;注释 pause&#xff1a;暂停正在执行的批处理文件&#xff0c;并提示用户按键之后继续执行 r…

VisualStudio2017社区版安装完毕后,找不到stdio.h等头文件的解决方案

安装完VisualStudio2017社区版后&#xff0c;创建一个C的测试项目&#xff0c;好使&#xff0c;一时心血来潮创建了纯C的项目&#xff0c;结果死活提示找不到头文件“stdio.h”&#xff0c;测试代码如下&#xff1a; #include<stdio.h>int main() {printf("Hello w…

超详细 | CISP-信息安全专业认证考前攻略

近年来&#xff0c;中央关于信息安全的政策不断加码&#xff0c;网络安全已上升到国家战略&#xff0c;安全专业人才缺口以每年1.5万人的速度递增。作为信息安全从业人员&#xff0c;持证上岗已是大势所趋。今天&#xff0c;重点聊一下信息安全国内第一认证CISP&#xff01; 注…

关于表单快速开发低代码技术平台的内容介绍

运用什么样的表单快速开发软件平台可以实现高效率创收&#xff1f;随着科技的进步和飞速发展&#xff0c;专业的低代码技术平台已经走入了很多企业的办公职场中&#xff0c;它们灵活、轻量级、优质、高效、易维护等优势特点&#xff0c;可以高效助力广大企业提质增效&#xff0…

php代码审计篇bluecms代码审计

文章目录 Seay自动审计源码分析/ad_js.php文件sql注入分析/include/common.fun.php注入分析/admin/login.php存在宽字节注入分析/admin/nav.php SQL注入分析前台注册界面存在存储型xss Seay自动审计 使用seay进行自动代码审计 源码分析 /ad_js.php文件sql注入分析 查看执行…

k8s-部署

1.k8s 集群与部署 更改所有主机名字和解析 k8s1 192.168.25.11 reg.westos.org,habbor 仓库 k8s2 192.168.25.12 master&#xff0c;k8s 集群控制节点 k8s3 192.168.25.13 node&#xff0c;k8s 集群工作节点 k8s4 192.168.25.14 node&#xff0c;k8s 集群工作节点 所有节…

不再跳票Fedora 26 正式发布!

经过延期和跳票&#xff0c;Fedora 26终于和大家见面了&#xff0c;下面是Fedora 项目负责人Matthew Miller感谢信 大家好&#xff0c;我很高兴地宣布&#xff0c;从即刻起 Fedora 26 正式可用了。你可以从下面了解到具体信息&#xff0c;也可以马上开始下载&#xff1a; •下载…

漫画工厂ai-comic-factory 文字2漫画

demo入口https://huggingface.co/spaces/jbilcke-hf/ai-comic-factory 最终展示 大概流程&#xff1a; 选漫画分格输入需要将啥故事X X 通过Llama2 70B 生成具体的每个分割图的描述YY 通过SDXL 生成图 LLM: llama-2 is used to generate the captions of 4 comic panels (pro…

【从0学习Solidity】 6. 引用类型, array, struct

【从0学习Solidity】 6. 引用类型, array, struct 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&…

软考证书可以评职称吗?怎么评?

软考是可以帮助评职称的&#xff0c;取得软考证书&#xff0c;就具备评职称的相应资格。 通过软考获得证书的人员&#xff0c;表明其已具备从事相应专业岗位工作的水平和能力&#xff0c;用人单位可根据工作需要从获得证书的人员中择优聘任相应专业技术职务&#xff08;技术员…

Python函数绘图与高等代数互融实例(三):设置X|Y轴文本标签|网格线

Python函数绘图与高等代数互融实例(一):正弦函数与余弦函数 Python函数绘图与高等代数互融实例(二):闪点函数 Python函数绘图与高等代数互融实例(三):设置X|Y轴|网格线 Python函数绘图与高等代数互融实例(四):设置X|Y轴参考线|参考区域 一: 设置X|Y轴文本标签 import num…

Python在工业自动化领域的应用详解

概要 当我们开始讨论在工业自动化应用中使用哪种编程语言时&#xff0c;通常我们会首先谈论IEC 61131-3标准中用于可编程逻辑控制器&#xff08;PLC&#xff09;的语言&#xff0c;比如经典的梯形图&#xff08;LD&#xff09;或结构化文本&#xff08;ST&#xff09;。对于机器…

OceanBase Docker体验

实验一&#xff1a;OceanBase Docker体验 通过 OceanBase Docker 容器&#xff0c;快速的体验 OceanBase 的 自动化部署过程&#xff0c;以及了解 OceanBase 集群安装成功后的目录特点和使用方法。 Docker镜像 实验环境 实验环境说明 1台OCP 5台OBSERVER aarch64 Kylin Lin…

【效率提升】maven 转 gradle 实战 | 京东云技术团队

一、灵魂三问 1、gradle 是什么&#xff1f; 一个打包工具&#xff0c; 是一个开源构建自动化工具&#xff0c;足够灵活&#xff0c;可以构建几乎任何类型的软件&#xff0c;高性能、可扩展、能洞察等。其中洞察&#xff0c;可以用于分析构建过程中数据&#xff0c;提供分析参…