【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

news2025/6/12 6:14:42

前言:

   双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。

目录

​编辑

前言:

类加载器

1. 启动类加载器(Bootstrap Class Loader)

2. 扩展类加载器(Extension Class Loader)

3. 应用类加载器(Application Class Loader)

4. 自定义类加载器(Custom Class Loader)

双亲委派机制

1. 基本概念

(1)避免类重复加载

(2)保护核心 API

(3)实现类的隔离与模块化

工作流程

双亲委派机制源码解读:

ClassLoader 的四个核心方法

核心问题

总结


类加载器

由于在jdk8以及之前没有模块化这个概念,所以jkd8之前和jdk9之后加载器有些变化,在这里介绍的是jdk8及之前的加载器。

在 Java 中,类加载器(Class Loader)负责将类的字节码(.class 文件)加载到 JVM 中。根据层级和职责,主要分为以下几类:

1. 启动类加载器(Bootstrap Class Loader)

  • 职责:加载 JVM 核心类库(如 java.lang.*java.util.*),路径通常为 $JAVA_HOME/jre/lib
  • 特点
    • 由 JVM 底层实现(C++ 编写),在 Java 代码中无法直接引用。
    • 加载最基础的类,确保 JVM 运行环境。

2. 扩展类加载器(Extension Class Loader)

  • 职责:加载 Java 扩展库(如 javax.* 包),路径为 $JAVA_HOME/jre/lib/ext
  • 特点
    • 是 java.lang.ClassLoader 的子类(Java 实现)。
    • 负责加载标准库之外的扩展功能。

3. 应用类加载器(Application Class Loader)

  • 职责:加载用户类路径(classpath)下的类,即开发者编写的代码。
  • 特点
    • 是 ClassLoader 的子类,也称为系统类加载器。
    • 是 ClassLoader.getSystemClassLoader() 的返回值。

4. 自定义类加载器(Custom Class Loader)

  • 职责:通过继承 java.lang.ClassLoader 实现,用于加载特殊来源的类(如网络、加密文件)。
  • 常见场景
    • 热部署(动态加载类)。
    • 加载自定义格式的字节码(如加密类)。
    • 实现类的隔离(如 OSGi 框架)。

注意点:不能认为一个加载器与其父类加载器的关系是继承,虽然有“父”。

需要注意的是如果我们尝试获取扩展类加载器的parent对象,得到的结果是null。并不是说其父类不是启动类加载器,只是因为启动类加载器属于JVM的一部分,使用C++实现,无法被获取到 

双亲委派机制

1. 基本概念

双亲委派指的是:当一个类加载器收到类加载请求时,它首先会将请求委派给父类加载器去尝试加载,而非立即自己加载。只有当父类加载器无法加载该类时,子类加载器才会尝试自己加载。

设计目的

(1)避免类重复加载

若多个类加载器都尝试加载同一个类,会导致内存中存在多个相同类的实例,破坏类型唯一性。双亲委派确保类只被加载一次。

(2)保护核心 API

例如,用户自定义的 java.lang.String 类不会被加载,因为 String 由启动类加载器加载。这防止恶意或错误代码覆盖 JVM 核心类,保障系统安全。

(3)实现类的隔离与模块化

不同类加载器可加载同名但不同路径的类(如插件系统),实现隔离性。例如,Web 容器(如 Tomcat)使用自定义类加载器实现多个应用的隔离。

核心原则

  • 向上委派:先让父类加载器尝试加载。
  • 向下查找:父类无法加载时,子类再尝试。

工作流程

当类加载器(如 AppClassLoader)收到类加载请求时:

  1. 检查缓存:首先检查该类是否已被加载。
  2. 委派父类:若未加载,则将请求委派给父类加载器(如 ExtClassLoader)。
  3. 递归委派:父类加载器重复步骤 1 和 2,直到到达启动类加载器。
  4. 尝试加载
    • 启动类加载器尝试加载该类,若成功则返回 Class 对象。
    • 若失败,则由扩展类加载器尝试,依此类推,直到子类加载器(如 AppClassLoader)。
  5. 抛出异常:若所有类加载器都无法加载,则抛出 ClassNotFoundException

双亲委派机制源码解读:

ClassLoader 的四个核心方法

  • loadClass:负责整体调度,按层级查找类。
  • findClass:具体去哪里找类(子类必须实现)。
  • defineClass:把类文件的二进制内容变成 JVM 能懂的 Class 对象。
  • resolveClass:解析类的内部结构,确保类能正常工作。

在加载类的时候会调用它,第一个参数是加载的类名,第二个参数是否需要解析类

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
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

代码流程:

  1. 获取加载的类,如果为空说明没有加载。
  2. 进入第一个if,判断有无父类加载器,有就调用父类加载器加载。
  3. 没有父类加载器说明需要用启动类加载器加载。
  4. 第二个if表示父类加载器加载失败,则调用此时的子类加载器加载。

我们来看看findclass方法的源码

protected Class<?> findClass(String name) throws ClassNotFoundException {

        int index = name.indexOf(';');
        String cookie = "";
        if(index != -1) {
                cookie = name.substring(index, name.length());
                name = name.substring(0, index);
        }

        // check loaded JAR files
        try {
            return super.findClass(name);
        } catch (ClassNotFoundException e) {
        }

        // Otherwise, try loading the class from the code base URL

        // 4668479: Option to turn off codebase lookup in AppletClassLoader
        // during resource requests. [stanley.ho]
        if (codebaseLookup == false)
            throw new ClassNotFoundException(name);

//      final String path = name.replace('.', '/').concat(".class").concat(cookie);
        String encodedName = ParseUtil.encodePath(name.replace('.', '/'), false);
        final String path = (new StringBuffer(encodedName)).append(".class").append(cookie).toString();
        try {
            byte[] b = AccessController.doPrivileged(
                               new PrivilegedExceptionAction<byte[]>() {
                public byte[] run() throws IOException {
                   try {
                        URL finalURL = new URL(base, path);

                        // Make sure the codebase won't be modified
                        if (base.getProtocol().equals(finalURL.getProtocol()) &&
                            base.getHost().equals(finalURL.getHost()) &&
                            base.getPort() == finalURL.getPort()) {
                            return getBytes(finalURL);
                        }
                        else {
                            return null;
                        }
                    } catch (Exception e) {
                        return null;
                    }
                }
            }, acc);

            if (b != null) {
                return defineClass(name, b, 0, b.length, codesource);
            } else {
                throw new ClassNotFoundException(name);
            }
        } catch (PrivilegedActionException e) {
            throw new ClassNotFoundException(name, e.getException());
        }
    }

核心问题

什么是双亲委派机制?

1.在一个类加载器去加载一个类的时候,会自下而上去查找这个类有没有被加载过,如果都没有加载,就会自上而下去加载这个类,如果都无法加载就会抛出异常。

2.应用类加载器的父类是扩展类加载器,扩展类加载器的父类是启动类加载器。

3.双亲委派机制的核心是第一个确保类不会被重复加载,第二个避免恶意代码替换JDK的核心类库,保证核心类库的安全和完整。

总结

     双亲委派机制是 Java 安全模型的基石,它通过层级委派和缓存机制确保类的唯一性和安全性。理解该机制有助于排查类加载异常(如 NoClassDefFoundError),并设计出更健壮的框架和应用。

     我在学到defineClass的时候有一点疑问,在源代码执行过程中就已经编译成为了class文件,为什么还需要defineClass后来我发现,编译后的是class文件是二进制形式的,而defineClass是把它转换为JVM能识别的class对象。虽然都是class但是内容形式并不一样。

这也体现了写博客的好处,方便查漏补缺,感谢你们的阅读,你的阅读和点赞是我最大的动力。

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

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

相关文章

c++第七天 继承与派生2

这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分&#xff1a;派生类构造函数与析构函数 当创建一个派生类对象时&#xff0c;基类成员是如何初始化的&#xff1f; 1.当派生类对象创建的时候&#xff0c;基类成员的初始化顺序 …

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台

淘宝扭蛋机小程序系统的开发&#xff0c;旨在打造一个互动性强的购物平台&#xff0c;让用户在购物的同时&#xff0c;能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机&#xff0c;实现旋转、抽拉等动作&#xff0c;增…

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storms…

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用

在工业制造领域&#xff0c;无损检测&#xff08;NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统&#xff0c;以非接触式光学麦克风技术为核心&#xff0c;打破传统检测瓶颈&#xff0c;为半导体、航空航天、汽车制造等行业提供了高灵敏…

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…

论文阅读笔记——Muffin: Testing Deep Learning Libraries via Neural Architecture Fuzzing

Muffin 论文 现有方法 CRADLE 和 LEMON&#xff0c;依赖模型推理阶段输出进行差分测试&#xff0c;但在训练阶段是不可行的&#xff0c;因为训练阶段直到最后才有固定输出&#xff0c;中间过程是不断变化的。API 库覆盖低&#xff0c;因为各个 API 都是在各种具体场景下使用。…

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …

关于easyexcel动态下拉选问题处理

前些日子突然碰到一个问题&#xff0c;说是客户的导入文件模版想支持部分导入内容的下拉选&#xff0c;于是我就找了easyexcel官网寻找解决方案&#xff0c;并没有找到合适的方案&#xff0c;没办法只能自己动手并分享出来&#xff0c;针对Java生成Excel下拉菜单时因选项过多导…

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…

wpf在image控件上快速显示内存图像

wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像&#xff08;比如分辨率3000*3000的图像&#xff09;的办法&#xff0c;尤其是想把内存中的裸数据&#xff08;只有图像的数据&#xff0c;不包…