速读摘要文章源自JAVA秀-https://www.javaxiu.com/22031.html
最近在做类隔离相关的一些工作,而恰恰之前协助开发同学时也发现会遇到许多类加载相关的异常,并且往往比较难定位与解决。还有另外一种情况是当一个类已经被某个类加载器加载到内存中,另外一个类加载器试图去加载时也会发生错误。解决这个问题的办法包括2种,即控制不同类加载器加载的类不进行交互,或者都交于一个共同的父加载器进行加载。NoSuchMethodError是因为加载了不正确的类,LinkageError则是由于同一个类被多个类加载器加载所导致的。文章源自JAVA秀-https://www.javaxiu.com/22031.html
原文约 3069 字 | 图片 5 张 | 建议阅读 7 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/22031.html
类加载常见错误总结,写得非常好!
点击关注 ? 小哈学Java 文章源自JAVA秀-https://www.javaxiu.com/22031.html
点击上方蓝色“小哈学Java”,选择“设为星标”回复“资源”获取独家整理的学习资料!
作者:fredalxin地址:https://fredal.xin/classloader-error文章源自JAVA秀-https://www.javaxiu.com/22031.html
文章源自JAVA秀-https://www.javaxiu.com/22031.html
最近在做类隔离相关的一些工作,而恰恰之前协助开发同学时也发现会遇到许多类加载相关的异常,并且往往比较难定位与解决。这里简单做一个小总结。文章源自JAVA秀-https://www.javaxiu.com/22031.html
类加载
首先我们来捋一捋类加载的基础知识。文章源自JAVA秀-https://www.javaxiu.com/22031.html
以上是大家比较熟悉的类加载器模型,主要包含 3 种类加载器:文章源自JAVA秀-https://www.javaxiu.com/22031.html
BootstrapClassloader 根加载器,也就是系统类加载器,加载核心库,如 rt.jar。文章源自JAVA秀-https://www.javaxiu.com/22031.html
ExtensionClassloader 扩展类加载器,主要加载/ext/下面的 jar 包文章源自JAVA秀-https://www.javaxiu.com/22031.html
AppClassloader 离我们最近的类加载器,负责加载 classpath 下的类,开发时候我们的代码大部分由其加载。文章源自JAVA秀-https://www.javaxiu.com/22031.html
此外我们比较需要知道的几点:文章源自JAVA秀-https://www.javaxiu.com/22031.html
一个类是由 jvm 加载是通过类加载器+全限定类名确定唯一性的。文章源自JAVA秀-https://www.javaxiu.com/22031.html
双亲委派,众所周知,子加载器会尽量委托给父加载器进行加载,父加载器找不到再自己加载文章源自JAVA秀-https://www.javaxiu.com/22031.html
线程上下文类加载,为了满足 spi 等需求突破双亲委派机制,当高层类加载器想加载底层类时通过 Thread.contextClassLoader 来获取当前线程的类加载器(往往是底层类加载器)去加载类。文章源自JAVA秀-https://www.javaxiu.com/22031.html
ClassNotFoundException
ClassNotFoundException 表示类找不到异常,是一种 Exception,通常发生在载入阶段,当开发者主动调用 Class.forName()
、ClassLoader.loadClass()
或 ClassLoader.findSystemClass()
动态加载指定类时候,类加载器就会去 classpath 下寻找类,如果找不到就会抛出此错误。文章源自JAVA秀-https://www.javaxiu.com/22031.html
还有另外一种情况是当一个类已经被某个类加载器加载到内存中,另外一个类加载器试图去加载时也会发生错误。文章源自JAVA秀-https://www.javaxiu.com/22031.html
ClassNotFoundException 是一个 exception 类,同时发生在主动执行动态加载时,所以我们应该去 catch 它,防止发生一些运行时错误。文章源自JAVA秀-https://www.javaxiu.com/22031.html
NoClassDefFoundError
NoClassDefFoundError 是一种和 ClassNotFoundException 很像的错误,只不过它是更严重的 error 类型。它发生在链接阶段,表示 jvm 在编译阶段可以找到相应的类,但在执行过程中却找不到相应的类。文章源自JAVA秀-https://www.javaxiu.com/22031.html
一种原因是由于在编译后运行前类被更改或者删除了。另外一种则是 classpath 本身被修改过了,这可以通过System.getProperty("java.classpath")
来找到程序实际运行的 classpath,或者通过-classpath 命令来指定正确的 classpath。文章源自JAVA秀-https://www.javaxiu.com/22031.html
那如果是在 ide 中开发,很多时候出现的情况是我们可以通过 ide 编译通过,但在实际运行的 WEB-INF/lib 下却是没有的。所以排查的时候我们需要去实际的 war 包下面确定是否有类。文章源自JAVA秀-https://www.javaxiu.com/22031.html
NoSuchMethodError
我们还会遇到 NoSuchMethodError 错误,它表示找不到方法,但找不到方法归根结底是找到了不正确的类。文章源自JAVA秀-https://www.javaxiu.com/22031.html
通常情况下是因为 jar 包冲突问题,即加载了不匹配版本的类导致的。例如应用中有 A、B 两个二方包,A 依赖 C-v1 包,而 B 依赖 C-v2 包,如果 maven 仲裁最后使用的是 C-v1 包,那么当 B 加载到 C-v2 中有而 C-v1 中没有的方法时就会报 NoSuchMethodError。文章源自JAVA秀-https://www.javaxiu.com/22031.html
这种情况我们首先得知道 jvm 到底加载的是什么版本,这可以使用-verbose:class
来确定。文章源自JAVA秀-https://www.javaxiu.com/22031.html
LinkageError
LinkageError 相比较之前几种错误不那么常见,只有多个类加载器同时作用交互时才会出现。文章源自JAVA秀-https://www.javaxiu.com/22031.html
我们知道 jvm 中一个类由全限定类名与类加载器确定类实例,那么不同类加载器加载的同一个类是属于不同类实例的,然后在内存中如果两者发生交互,就会出现 LinkageError 异常。文章源自JAVA秀-https://www.javaxiu.com/22031.html
一般情况下,jvm 加载类都会遵循之前所述的双亲委派原则,不太可能出现一个类有不同类加载器加载的情况。但在诸如 tomcat 之类的 javaEE 环境中,常常出这种状况,这是由于 tomcat 上的 web 应用类加载机制稍有不同,每个资源模块(比如一个 war 包)都优先使用自身的资源,突破了双亲委派模型:文章源自JAVA秀-https://www.javaxiu.com/22031.html
文章源自JAVA秀-https://www.javaxiu.com/22031.html
当 appClassLoader 加载类时候,会首先在自己的本地资源库中查找类,其次才会走双亲委派模型。那么如果一个类 A 由 AppClassLoaderx 加载,但其超类在 AppClassLoader 中没有,只有委托 CommonClassLoader 才能找到,当类 A 与其超类进行交互时就会报错了。文章源自JAVA秀-https://www.javaxiu.com/22031.html
还有一种比较常见的情况是进行自定义类加载器开发时遇到。比如开发类隔离容器时,期望将某些中间件都由与应用不同的独立类加载器加载,但这时候如果中间件依赖 spring context,而应用本身也依赖 spring context,那么 作为 spring bean 交互时候就会妥妥报 LinkageError 了。文章源自JAVA秀-https://www.javaxiu.com/22031.html
解决这个问题的办法包括 2 种,即控制不同类加载器加载的类不进行交互,或者都交于一个共同的父加载器进行加载。文章源自JAVA秀-https://www.javaxiu.com/22031.html
Some Tips
总结一下以上几种错误。ClassNotFoundException 以及 NoClassDefFoundError 都是由于加载不到类导致的,而 NoSuchMethodError 是因为加载了不正确的类,LinkageError 则是由于同一个类被多个类加载器加载所导致的。文章源自JAVA秀-https://www.javaxiu.com/22031.html
以上这些问题都可以使用arthas进行排查。例如使用 sc 命令来查看 JVM 已加载的类信息,包括从哪个 jar 包读取,由哪个类加载器加载。使用 jad 命令来查看 jvm 中反编译的代码,可以定位到底到底有没有所需 method。以及使用 classloader 命令,来查看当前所有 classloader 的信息,包括加载的 urls,是否能加载到指定的类或者 resources 等。文章源自JAVA秀-https://www.javaxiu.com/22031.html
1. SpringBoot 实现 MySQL 读写分离技术2. 大厂,常用,四款,大屏可视化工具3. Nginx 常用配置清单4. 当MyBatis 3.5.X遇上JDK8竟然出现了性能问题,全项目组都得加班~最近面试BAT,整理一份面试资料《Java面试BATJ通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 Java 领取,更多内容陆续奉上。
文章有帮助的话,在看,转发吧。文章源自JAVA秀-https://www.javaxiu.com/22031.html
谢谢支持哟 (*^__^*)文章源自JAVA秀-https://www.javaxiu.com/22031.html

评论