做者:fredalxin\
地址:https://fredal.xin/classloade...java
最近在作类隔离相关的一些工做,而偏偏以前协助开发同窗时也发现会遇到许多类加载相关的异常,而且每每比较难定位与解决。这里简单作一个小总结。web
首先咱们来捋一捋类加载的基础知识。面试
以上是你们比较熟悉的类加载器模型,主要包含 3 种类加载器:spring
此外咱们比较须要知道的几点:tomcat
ClassNotFoundException 表示类找不到异常,是一种 Exception,一般发生在载入阶段,当开发者主动调用 Class.forName()
、ClassLoader.loadClass()
或 ClassLoader.findSystemClass()
动态加载指定类时候,类加载器就会去 classpath 下寻找类,若是找不到就会抛出此错误。intellij-idea
还有另一种状况是当一个类已经被某个类加载器加载到内存中,另一个类加载器试图去加载时也会发生错误。app
ClassNotFoundException 是一个 exception 类,同时发生在主动执行动态加载时,因此咱们应该去 catch 它,防止发生一些运行时错误。jvm
NoClassDefFoundError 是一种和 ClassNotFoundException 很像的错误,只不过它是更严重的 error 类型。它发生在连接阶段,表示 jvm 在编译阶段能够找到相应的类,但在执行过程当中却找不到相应的类。maven
一种缘由是因为在编译后运行前类被更改或者删除了。另一种则是 classpath 自己被修改过了,这能够经过System.getProperty("java.classpath")
来找到程序实际运行的 classpath,或者经过-classpath 命令来指定正确的 classpath。ide
那若是是在 ide 中开发,不少时候出现的状况是咱们能够经过 ide 编译经过,但在实际运行的 WEB-INF/lib 下倒是没有的。因此排查的时候咱们须要去实际的 war 包下面肯定是否有类。
咱们还会遇到 NoSuchMethodError 错误,它表示找不到方法,但找不到方法归根结底是找到了不正确的类。
一般状况下是由于 jar 包冲突问题,即加载了不匹配版本的类致使的。例如应用中有 A、B 两个二方包,A 依赖 C-v1 包,而 B 依赖 C-v2 包,若是 maven 仲裁最后使用的是 C-v1 包,那么当 B 加载到 C-v2 中有而 C-v1 中没有的方法时就会报 NoSuchMethodError。
这种状况咱们首先得知道 jvm 到底加载的是什么版本,这可使用-verbose:class
来肯定。
LinkageError 相比较以前几种错误不那么常见,只有多个类加载器同时做用交互时才会出现。
咱们知道 jvm 中一个类由全限定类名与类加载器肯定类实例,那么不一样类加载器加载的同一个类是属于不一样类实例的,而后在内存中若是二者发生交互,就会出现 LinkageError 异常。
通常状况下,jvm 加载类都会遵循以前所述的双亲委派原则,不太可能出现一个类有不一样类加载器加载的状况。但在诸如 tomcat 之类的 javaEE 环境中,经常出这种情况,这是因为 tomcat 上的 web 应用类加载机制稍有不一样,每一个资源模块(好比一个 war 包)都优先使用自身的资源,突破了双亲委派模型:
当 appClassLoader 加载类时候,会首先在本身的本地资源库中查找类,其次才会走双亲委派模型。那么若是一个类 A 由 AppClassLoaderx 加载,但其超类在 AppClassLoader 中没有,只有委托 CommonClassLoader 才能找到,当类 A 与其超类进行交互时就会报错了。
还有一种比较常见的状况是进行自定义类加载器开发时遇到。好比开发类隔离容器时,指望将某些中间件都由与应用不一样的独立类加载器加载,但这时候若是中间件依赖 spring context,而应用自己也依赖 spring context,那么 做为 spring bean 交互时候就会妥妥报 LinkageError 了。
解决这个问题的办法包括 2 种,即控制不一样类加载器加载的类不进行交互,或者都交于一个共同的父加载器进行加载。
总结一下以上几种错误。ClassNotFoundException 以及 NoClassDefFoundError 都是因为加载不到类致使的,而 NoSuchMethodError 是由于加载了不正确的类,LinkageError 则是因为同一个类被多个类加载器加载所致使的。
以上这些问题均可以使用arthas进行排查。例如使用 sc 命令来查看 JVM 已加载的类信息,包括从哪一个 jar 包读取,由哪一个类加载器加载。使用 jad 命令来查看 jvm 中反编译的代码,能够定位到底到底有没有所需 method。以及使用 classloader 命令,来查看当前全部 classloader 的信息,包括加载的 urls,是否能加载到指定的类或者 resources 等。
近期热文推荐:
1.600+ 道 Java面试题及答案整理(2021最新版)
2.终于靠开源项目弄到 IntelliJ IDEA 激活码了,真香!
3.阿里 Mock 工具正式开源,干掉市面上全部 Mock 工具!
4.Spring Cloud 2020.0.0 正式发布,全新颠覆性版本!
以为不错,别忘了随手点赞+转发哦!