作者:fredalxin\
地址:https://fredal.xin/classloade…
最近在做类隔离相干的一些工作,而恰好之前帮助开发同学时也发现会遇到许多类加载相干的异样,并且往往比拟难定位与解决。这里简略做一个小总结。
类加载
首先咱们来捋一捋类加载的基础知识。
以上是大家比拟相熟的类加载器模型,次要蕴含 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 包)都优先应用本身的资源,冲破了双亲委派模型:
当 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 等。
近期热文举荐:
1.600+ 道 Java 面试题及答案整顿(2021 最新版)
2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!
3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!