【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言:
双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。
目录
编辑
前言:
类加载器
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
)收到类加载请求时:
- 检查缓存:首先检查该类是否已被加载。
- 委派父类:若未加载,则将请求委派给父类加载器(如
ExtClassLoader
)。- 递归委派:父类加载器重复步骤 1 和 2,直到到达启动类加载器。
- 尝试加载:
- 启动类加载器尝试加载该类,若成功则返回
Class
对象。- 若失败,则由扩展类加载器尝试,依此类推,直到子类加载器(如
AppClassLoader
)。- 抛出异常:若所有类加载器都无法加载,则抛出
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 loadedClass<?> 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 statsPerfCounter.getParentDelegationTime().addTime(t1 - t0);PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}
代码流程:
- 获取加载的类,如果为空说明没有加载。
- 进入第一个if,判断有无父类加载器,有就调用父类加载器加载。
- 没有父类加载器说明需要用启动类加载器加载。
- 第二个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 filestry {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 modifiedif (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但是内容形式并不一样。
这也体现了写博客的好处,方便查漏补缺,感谢你们的阅读,你的阅读和点赞是我最大的动力。
相关文章:

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...

淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...

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

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

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: 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、一个结构体实现多…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...

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

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

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...