类加载常见错误总结,写得非常好!

简介: 最近在做类隔离相关的一些工作,而恰恰之前协助开发同学时也发现会遇到许多类加载相关的异常,并且往往比较难定位与解决。这里简单做一个小总结。

最近在做类隔离相关的一些工作,而恰恰之前协助开发同学时也发现会遇到许多类加载相关的异常,并且往往比较难定位与解决。这里简单做一个小总结。


类加载

首先我们来捋一捋类加载的基础知识。

image.png

以上是大家比较熟悉的类加载器模型,主要包含 3 种类加载器:


BootstrapClassloader 根加载器,也就是系统类加载器,加载核心库,如 rt.jar。

ExtensionClassloader 扩展类加载器,主要加载/ext/下面的 jar 包

AppClassloader 离我们最近的类加载器,负责加载 classpath 下的类,开发时候我们的代码大部分由其加载。

此外我们比较需要知道的几点:


一个类是由 jvm 加载是通过类加载器+全限定类名确定唯一性的。

双亲委派,众所周知,子加载器会尽量委托给父加载器进行加载,父加载器找不到再自己加载

线程上下文类加载,为了满足 spi 等需求突破双亲委派机制,当高层类加载器想加载底层类时通过 Thread.contextClassLoader 来获取当前线程的类加载器(往往是底层类加载器)去加载类。

ClassNotFoundException

ClassNotFoundException 表示类找不到异常,是一种 Exception,通常发生在载入阶段,当开发者主动调用 Class.forName()、ClassLoader.loadClass()或 ClassLoader.findSystemClass()动态加载指定类时候,类加载器就会去 classpath 下寻找类,如果找不到就会抛出此错误。


还有另外一种情况是当一个类已经被某个类加载器加载到内存中,另外一个类加载器试图去加载时也会发生错误。


ClassNotFoundException 是一个 exception 类,同时发生在主动执行动态加载时,所以我们应该去 catch 它,防止发生一些运行时错误。


NoClassDefFoundError

NoClassDefFoundError 是一种和 ClassNotFoundException 很像的错误,只不过它是更严重的 error 类型。它发生在链接阶段,表示 jvm 在编译阶段可以找到相应的类,但在执行过程中却找不到相应的类。


一种原因是由于在编译后运行前类被更改或者删除了。另外一种则是 classpath 本身被修改过了,这可以通过System.getProperty("java.classpath")来找到程序实际运行的 classpath,或者通过-classpath 命令来指定正确的 classpath。


那如果是在 ide 中开发,很多时候出现的情况是我们可以通过 ide 编译通过,但在实际运行的 WEB-INF/lib 下却是没有的。所以排查的时候我们需要去实际的 war 包下面确定是否有类。


NoSuchMethodError

我们还会遇到 NoSuchMethodError 错误,它表示找不到方法,但找不到方法归根结底是找到了不正确的类。


通常情况下是因为 jar 包冲突问题,即加载了不匹配版本的类导致的。例如应用中有 A、B 两个二方包,A 依赖 C-v1 包,而 B 依赖 C-v2 包,如果 maven 仲裁最后使用的是 C-v1 包,那么当 B 加载到 C-v2 中有而 C-v1 中没有的方法时就会报 NoSuchMethodError。


这种情况我们首先得知道 jvm 到底加载的是什么版本,这可以使用-verbose:class来确定。


LinkageError

LinkageError 相比较之前几种错误不那么常见,只有多个类加载器同时作用交互时才会出现。


我们知道 jvm 中一个类由全限定类名与类加载器确定类实例,那么不同类加载器加载的同一个类是属于不同类实例的,然后在内存中如果两者发生交互,就会出现 LinkageError 异常。


一般情况下,jvm 加载类都会遵循之前所述的双亲委派原则,不太可能出现一个类有不同类加载器加载的情况。但在诸如 tomcat 之类的 javaEE 环境中,常常出这种状况,这是由于 tomcat 上的 web 应用类加载机制稍有不同,每个资源模块(比如一个 war 包)都优先使用自身的资源,突破了双亲委派模型:


image.png


当 appClassLoader 加载类时候,会首先在自己的本地资源库中查找类,其次才会走双亲委派模型。那么如果一个类 A 由 AppClassLoaderx 加载,但其超类在 AppClassLoader 中没有,只有委托 CommonClassLoader 才能找到,当类 A 与其超类进行交互时就会报错了。


还有一种比较常见的情况是进行自定义类加载器开发时遇到。比如开发类隔离容器时,期望将某些中间件都由与应用不同的独立类加载器加载,但这时候如果中间件依赖 spring context,而应用本身也依赖 spring context,那么 作为 spring bean 交互时候就会妥妥报 LinkageError 了。


解决这个问题的办法包括 2 种,即控制不同类加载器加载的类不进行交互,或者都交于一个共同的父加载器进行加载。


Some Tips

总结一下以上几种错误。ClassNotFoundException 以及 NoClassDefFoundError 都是由于加载不到类导致的,而 NoSuchMethodError 是因为加载了不正确的类,LinkageError 则是由于同一个类被多个类加载器加载所导致的。


以上这些问题都可以使用arthas进行排查。例如使用 sc 命令来查看 JVM 已加载的类信息,包括从哪个 jar 包读取,由哪个类加载器加载。使用 jad 命令来查看 jvm 中反编译的代码,可以定位到底到底有没有所需 method。以及使用 classloader 命令,来查看当前所有 classloader 的信息,包括加载的 urls,是否能加载到指定的类或者 resources 等。


相关文章
|
小程序 JavaScript Android开发
【经验分享】如何在支付宝小程序里玩转富文本功能
【经验分享】如何在支付宝小程序里玩转富文本功能
811 6
|
8月前
|
弹性计算 机器人 应用服务中间件
一键部署开源Qwen3并集成到钉钉、企业微信
Qwen3系列模型现已正式发布并开源,包含8款“混合推理模型”,其中涵盖两款MoE模型(Qwen3-235B-A22B与Qwen3-30B-A3B)及六个Dense模型。阿里云计算巢已支持Qwen3-235B-A22B和Qwen3-32B的私有化部署,用户可通过计算巢轻松完成部署,并借助AppFlow集成至钉钉机器人或企业微信。文档详细介绍了从模型部署、创建应用到配置机器人的全流程,帮助用户快速实现智能助手的接入与使用。
721 19
一键部署开源Qwen3并集成到钉钉、企业微信
|
7月前
|
存储 监控 安全
云HIS系统的住院流程解析
云HIS是一种基于云计算的医院信息系统,无需本地服务器,通过互联网实现数据存储与处理。它覆盖患者住院全流程,包括入院登记、床位分配、医嘱管理、费用结算及出院准备,支持电子病历共享、权限管理和自动化报表生成。系统优化了住院环节,提供自备药处理、药品库存预警和退费流程等解决方案,提升医院运营效率与服务质量。
301 4
|
9月前
|
人工智能 自然语言处理 JavaScript
需要的效果它都有,让AI对话开发效率翻倍!这款Ant Design扩展组件库绝了
ant-design-x-vue 是基于 Ant Design Vue 的扩展组件库,专注于增强聊天和AI交互场景的体验。项目提供开箱即用的对话式UI组件,支持消息气泡、智能建议、思维链展示等特色功能,特别适合快速搭建智能客服、AI助手类应用。
878 5
|
敏捷开发 供应链 数据可视化
如何利用精益生产管理工具提升项目执行力?推荐7款必备工具
本文介绍了七款精益生产管理工具,包括板栗看板、LeanKit、Targetprocess、Miro、Smartsheet、Airtable 和 LiquidPlanner,详细阐述了各工具的功能亮点及其在不同行业的应用,旨在帮助企业提高效率、减少浪费、优化流程,实现项目管理的持续改进。
如何利用精益生产管理工具提升项目执行力?推荐7款必备工具
|
SQL 数据挖掘 数据库
SQL查询每秒的数据:技巧、方法与性能优化
id="">SQL查询功能详解 SQL(Structured Query Language,结构化查询语言)是一种专门用于与数据库进行沟通和操作的语言
|
存储 NoSQL MongoDB
【赵渝强老师】部署MongoDB复制集
本文介绍了如何在单个节点上搭建MongoDB复制集环境,通过监听不同端口实现多节点配置。详细步骤包括创建数据目录、编辑配置文件、启动节点、初始化复制集、查看状态以及测试主从库的读写操作。文中还提供了视频讲解和代码示例,帮助读者更好地理解和操作。
323 0
|
JSON 网络协议 前端开发
【UR六轴机械臂源码】python脱离示教器控制UR机械臂实时采集机器人位姿(优傲机器人)
【UR六轴机械臂源码】python脱离示教器控制UR机械臂实时采集机器人位姿(优傲机器人)
|
Prometheus 监控 Cloud Native
Docker下Prometheus和Grafana三部曲之三:自定义监控项开发和配置
本文详细讲述了业务系统如何将自定义的监控项上报到prometheus,并且在Grafana上配置监控项展示上报的自定义数据
687 0
Docker下Prometheus和Grafana三部曲之三:自定义监控项开发和配置
|
编译器 API C语言
C语言与硬件交互:通过I/O端口控制硬件。
C语言与硬件交互:通过I/O端口控制硬件。