深入理解JVM类加载器与双亲委派模型

发布时间:2026/10/8 13:04:10

深入理解JVM类加载器与双亲委派模型 前言在Java技术栈中类加载机制是每一位开发者都应该深入理解的核心知识。无论是日常开发中遇到的ClassNotFoundException还是框架层面的热部署、模块隔离背后都离不开类加载器与双亲委派模型的支撑。本文将带你从零开始系统性地理解JVM类加载器的分类、双亲委派模型的工作原理以及这一设计背后的深意。一、什么是类加载器类加载器ClassLoader是JVM运行时系统的重要组成部分负责在运行时查找字节码文件将字节码文件加载到内存中并将其转化为运行时数据结构。简单来说类加载器的主要作用就是动态加载Java类的字节码.class文件到JVM中在内存中生成一个代表该类的Class对象。字节码可以是Java源程序经过javac编译得来也可以是通过工具动态生成或者通过网络下载得来。值得注意的是JVM启动时并不会一次性加载所有的类而是根据需要动态加载——大部分类在具体用到时才会被加载这种方式对内存更加友好。二、类加载器的分类从JVM的角度看类加载器主要分为两大类Bootstrap ClassLoader和其他类加载器。从Java开发者的角度我们需要了解以下四种2.1 启动类加载器Bootstrap ClassLoader这是最顶层的类加载器由C实现是虚拟机自身的一部分。它负责加载JAVA_HOME/lib目录下的核心类库如rt.jar、resources.jar、charsets.jar等以及被-Xbootclasspath参数指定路径下的所有类。在Java代码中我们无法直接获取到Bootstrap ClassLoader的引用——通过getClassLoader()获取时通常返回null。我们常用的java.lang包下的类如String、Object都是由Bootstrap ClassLoader加载的。2.2 扩展类加载器Extension ClassLoader扩展类加载器由sun.misc.Launcher$ExtClassLoader实现负责加载JAVA_HOME/lib/ext目录下的类库或者被java.ext.dirs系统变量指定路径中的所有类库。2.3 应用程序类加载器Application ClassLoader应用程序类加载器由sun.misc.Launcher$AppClassLoader实现负责加载用户CLASSPATH环境变量指定路径中的所有类库。如果没有自定义类加载器这就是Java程序中默认使用的类加载器。2.4 用户自定义类加载器Custom ClassLoader用户可以通过继承java.lang.ClassLoader类来实现自定义类加载器。常见的应用场景包括隔离加载类、修改类加载方式、扩展类加载源如从网络或数据库加载、防止源代码泄露对字节码加密后自定义加载等。注意JDK 9之后引入了模块化系统扩展类加载器Extension ClassLoader被改名为平台类加载器Platform ClassLoader。三、双亲委派模型3.1 什么是双亲委派模型双亲委派模型Parent Delegation Model是JVM类加载器的核心工作机制。它要求除了顶层的启动类加载器外其余的类加载器都应当有自己的父类加载器。这里的父子关系一般不是通过继承实现的而是通过组合关系来复用父加载器的代码。一个小插曲其实双亲这个翻译容易让人误解——每个类加载器只有一个父加载器翻译成单亲委派可能更准确但双亲委派模型这个说法已经在国内广为流传我们就按这个来理解就好。3.2 工作流程双亲委派模型的工作流程可以概括为以下几个步骤第一步自底向上检查。当一个类加载器收到类加载请求时它首先检查这个类是否已经被自己加载过了。如果已加载直接返回否则将请求委派给父类加载器。每一层都如此直到最顶层的启动类加载器。第二步自顶向下尝试加载。如果启动类加载器在自己的加载路径中找到了这个类就加载并返回如果找不到就由下一层扩展类加载器尝试加载以此类推直到最初收到请求的那个类加载器自己尝试加载。第三步加载失败则抛出异常。如果所有类加载器都无法加载这个类最终抛出ClassNotFoundException。可以用一张图来帮助理解向上检查向下加载Bootstrap ClassLoader ↑ (委托) | (加载) Extension ClassLoader ↑ (委托) | (加载) Application ClassLoader ↑ (委托) | (加载) Custom ClassLoader3.3 源码实现双亲委派模型的实现逻辑集中在java.lang.ClassLoader的loadClass()方法中核心代码如下protectedClass?loadClass(Stringname,booleanresolve)throwsClassNotFoundException{synchronized(getClassLoadingLock(name)){// 首先检查这个类是否已经被加载过了Class?cfindLoadedClass(name);if(cnull){try{if(parent!null){// 存在父类加载器委托给父类加载cparent.loadClass(name,false);}else{// 父类加载器为null说明是启动类加载器cfindBootstrapClassOrNull(name);}}catch(ClassNotFoundExceptione){// 父类加载器无法完成加载}if(cnull){// 父类加载器无法加载自己尝试加载cfindClass(name);}}returnc;}}3.4 为什么要采用双亲委派模型双亲委派模型的设计并非偶然它主要解决了以下几个关键问题1. 避免类的重复加载。通过自底向上的检查机制同一个类在全JVM范围内只会被加载一次保证了类的唯一性。2. 保证核心类库的安全性。如果没有双亲委派模型用户完全可以自己编写一个java.lang.String类并放在ClassPath中导致系统出现多个不同的String类Java类型体系中最基础的行为将无法保证。有了双亲委派模型所有对java.lang.String的加载请求最终都会到达Bootstrap ClassLoader加载rt.jar中的标准String类。3. 保证Java平台的稳定性。核心类库总是由最顶层的类加载器加载确保了不同类加载器环境中核心类的一致性。四、打破双亲委派模型双亲委派模型并不是一种强制性的约束只是JDK官方推荐的一种方式。在某些场景下我们需要打破这一模型。4.1 第一次破坏JDK 1.2之前的兼容性双亲委派模型在JDK 1.2之后才被引入但ClassLoader抽象类在Java的第一个版本中就已经存在。为了兼容已有的自定义类加载器代码Java设计者在ClassLoader中新增了findClass()方法引导用户重写findClass()而不是loadClass()这样既保留了自定义能力又能让新的类加载器符合双亲委派规则。4.2 第二次破坏基础类型回调用户代码线程上下文类加载器双亲委派模型的一个天然缺陷是子加载器可以看到父加载器的内容但反之不行。如果基础类型由Bootstrap ClassLoader加载需要回调用户代码在ClassPath中由Application ClassLoader加载就会遇到问题。一个典型的例子是JNDI服务。JNDI的代码在JDK 1.3时被加入rt.jar由启动类加载器加载。但JNDI需要调用由第三方厂商实现并部署在ClassPath下的SPI服务提供者接口代码。启动类加载器无法加载这些代码。为了解决这个问题Java引入了线程上下文类加载器Thread Context ClassLoader。通过Thread.setContextClassLoader()方法设置后基础类可以通过这个加载器来加载用户代码。这实质上打破了双亲委派模型的单向层次结构。4.3 第三次破坏对动态性的追求OSGi与热部署OSGiOpen Service Gateway Initiative是实现模块化热部署的典型代表。在OSGi环境中每个模块Bundle都有自己的类加载器。当需要更换一个Bundle时只需将Bundle连同其类加载器一起换掉即可实现代码的热替换。OSGi的类加载器不再遵循双亲委派模型的树状结构而是发展成了更加复杂的网状结构。收到类加载请求时OSGi会按照特定的顺序在多个Bundle中搜索类。除此之外Tomcat为每个Web应用创建独立的WebAppClassLoader打破了双亲委派模型优先自己加载而非委托父加载器实现了不同Web应用之间的类隔离。五、总结类加载器与双亲委派模型是JVM体系中的核心设计之一。理解这一机制不仅有助于我们排查日常开发中遇到的类加载相关异常理解Spring、Tomcat等框架的类隔离与热部署原理在需要时实现自定义类加载器满足特定业务需求双亲委派模型通过层次化的委托机制巧妙地平衡了安全性防止核心类被篡改、一致性保证类的唯一性和灵活性允许自定义类加载器三者之间的关系。尽管在某些场景下它需要被打破但作为Java平台二十多年来稳定运行的重要基石双亲委派模型的设计思想依然值得我们深入品味与学习。
延伸阅读

更多相关文章

2026/10/5 21:16:31

CAR-T细胞治疗:技术演进、临床突破与转化挑战

简述 CAR-T疗法通过基因工程改造患者自身T细胞,使其表达嵌合抗原受体(CAR)以特异性识别并杀伤肿瘤细胞,在血液系统恶性肿瘤治疗中取得了里程碑式的突破。 一、CAR-T疗法的技术溯源与核心结构解析 CAR-T细胞治疗属于过继性细胞免疫…

2026/10/6 21:27:12

关于tty的发展历史和理解

tty是一部从笨重的机电设备进化为现代操作系统核心软件抽象的历史。它起源于19世纪的电传打字机(teletypewriter),为了满足远距离文本通信而生;最终在unix/linux系统中,演变为今日高度抽象的tty子系统。 一、发展历史 第一阶段:机电的巨人(1830s-1960s) 这个时代的t…

2026/10/5 21:17:19

AI时代网络安全新范式:从边界防护到行为监控的防御者窗口实践

当你的代码库被AI助手扫描、你的API密钥在日志中泄露、你的内部文档被大模型“学习”并对外泄露时,网络安全的第一道防线是否已经悄然失守?这不是危言耸听,而是每个将AI能力引入研发流程的团队正在面临的真实风险。 “OpenAI 防御者窗口”并…

2026/10/8 13:00:47

TPS259483AYWPR+ATmega32构建智能电源路径保护系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 13:00:47

eFuse+MCU实现智能电源保护:基于TPS259483与MK24FN256VDC12的设计

板子第一次上电,我最紧张的不是代码编译不过,而是电源路径上哪个小角落突然冒烟。做嵌入式这几年,挨过的板子多了之后你会发现,MCU再聪明、传感器再贵,电源一旦出问题,前面的努力全白费。这次项目核心就一件…

2026/10/8 13:00:47

连锁店管理系统:多门店数字化协同的选型思考

2026年实体连锁行业调研显示,超过六成多门店品牌正遭遇渠道数据割裂的现实难题,线下门店、线上小程序、外卖平台的经营数据相互独立,总部很难完整掌握每家门店真实经营状态。连锁店管理系统的核心价值,就是打通分散的业务节点&…

2026/10/8 13:00:47

Pi 的 Rust 实现来了:rpi,不止一个终端 Agent

如果你熟悉 Pi,rpi 可以理解为它的 Rust 实现:同样能在终端里读文件、改代码、执行命令,也能作为库嵌入自己的程序。 rpi 用 Rust 重新实现 Pi SDK,并在这套运行时之上提供终端 CLI 和原生扩展。 想直接使用编码助手,…

2026/10/8 12:55:47

拯救者花屏报43错误别急着换主板:先软后硬排查指南

1. 拯救者花屏报43错误,为什么我第一反应不是换主板拯救者系列游戏本用久了,花屏和显卡报43错误算是比较典型的一类故障。很多人一看到屏幕出现彩色条纹、画面撕裂,或者设备管理器里显卡旁边挂个黄色感叹号、属性里写着“该设备有问题&#x…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:02:17

自然数立方等于连续奇数之和:从证明到编程验证

十几年来我一直游走在数学科普和编程教学这两块内容之间,对“看起来像魔法、拆开全是数学”的结论总是格外敏感。最近翻资料时又撞见一句话:任何一个自然数 m 的立方,都可以写成 m 个连续奇数之和。2 的立方等于 3 加 5,3 的立方等…

2026/10/8 0:02:17

C#上位机SSH连接实战:用SSH.NET补齐超时、批量与密钥认证

简介:这是一份基于 C# 开发的 SSH 连接功能半成品工程,原本作为另一个主项目的子功能模块,现独立打包分享。工程采用 WinForms 界面,包含源码、解决方案、安装部署工程、NuGet 依赖包及说明文档,适合正在做远程连接、网…

2026/10/8 0:02:17

Java SpringBoot一体化智能售后系统设计与实现全解析

毕业设计年年做,Java Web 方向的题目翻来覆去就那么几个,但“一体化智能售后系统”这个题,每次看到我都觉得值得认真聊一聊。它不是一个简单 curd 堆出来的管理系统,而是把客户、工单、派单、处理、回访、统计整条链路串起来的一套…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑