当前位置: 首页 > 科技观察

类加载常见错误总结,写的很好!

时间:2023-03-18 20:49:26 科技观察

最近在做一些类隔离相关的工作,但是之前在协助开发同学的时候,也发现自己遇到了很多类加载相关的异常,而且往往很难定位解决。这是一个简短的总结。类加载首先,让我们回顾一下类加载的基础知识。以上就是大家比较熟悉的类加载器模型,主要包括3种加载器:BootstrapClassloader根加载器,即系统类加载器,加载核心库,如rt.jar。ExtensionClassloader是一个扩展类加载器,主要加载/ext/下的jar包。AppClassloader是离我们最近的类加载器,负责加载类路径下的类。我们的大部分代码在开发过程中都是由它加载的。另外,我们还需要知道几点:一个类被jvm加载是通过类加载器+全限定类名来判断唯一性的。双亲委托,众所周知,子加载器会尝试委托给父加载器进行加载。如果父加载器找不到,它会自己加载线程上下文类。为了满足spi等需求,突破了双亲委托机制。当高层类加载器要加载底层类时,使用Thread.contextClassLoader获取当前线程的类加载器(往往是底层类加载器)来加载类。ClassNotFoundExceptionClassNotFoundException表示找不到该类。这是一个异常,通常发生在加载阶段。当开发者主动调用Class.forName()、ClassLoader.loadClass()或ClassLoader.findSystemClass()动态加载指定的类时,该类被加载时,编译器会在classpath中查找该类,如果是则抛出该错误找不到。还有一种情况,当一个类已经被一个类加载器加载到内存中,而另一个类加载器试图加载它时,就会发生错误。ClassNotFoundException是主动执行动态加载时出现的异常类,所以我们应该捕获它,以防止出现一些运行时错误。另外,关注公众号Java技术栈,后台回复:JVM46,可以获得46页的JVM高清教程,很全。NoClassDefFoundErrorNoClassDefFoundError是一种非常类似于ClassNotFoundException的错误,只是它是一种更严重的错误类型。它发生在链接阶段,也就是说jvm在编译阶段可以找到对应的类,但是在执行过程中找不到对应的类。一个原因是由于类在编译后和运行前被更改或删除。另一个是类路径本身已被修改。您可以使用System.getProperty("java.classpath")找到程序实际运行的类路径,或者使用-classpath命令指定正确的类路径。如果是在ide中开发,很多时候会出现我们可以通过ide进行编译,但是在实际运行的WEB-INF/lib下是不存在的。所以在检查的时候,我们需要到实际的war包中去判断是否有类。NoSuchMethodError我们也得到了NoSuchMethodError错误,意思是找不到方法,但是找不到方法归结为找错类了。一般是jar包冲突导致的,即加载了版本不匹配的类。比如应用中有两个二级包A和B,A依赖C-v1包,B依赖C-v2包,如果maven仲裁最终使用C-v1包,那么当B是loadedintoC-v2thereis当C-v1中没有method时,会报NoSuchMethodError。在这种情况下,我们首先需要知道jvm正在加载什么版本,这可以通过使用-verbose:class来确定。Java系列教程及实践源码:https://github.com/javastacks/javastackLinkageErrorLinkageError相对于前面几类错误来说较少见,只有在多个类加载器同时交互时才会出现。另外,关注公众号Java技术栈,后台回复:面试,可以拿到我整理的JVM系列面试题及答案,很全。我们知道在jvm中一个类是由全限定类名和类加载器来确定类实例的,那么不同类加载器加载的同一个类属于不同的类实例,然后两者在内存中交互的话,LinkageError会出现异常。一般情况下,jvm加载类会遵循前面介绍的双亲委派原则,不太可能一个类被不同的类加载器加载。但是在tomcat等javaEE环境中,经常会出现这种情况。这是因为tomcat上的web应用类加载机制略有不同。每个资源模块(比如war包)先使用自己的资源,突破了Parentaldelegationmodel:appClassLoader在加载一个class的时候,会先在自己的本地资源库中查找该class,然后再按照parentaldelegationmodel进行加载。那么如果一个类A被AppClassLoaderx加载了,但是它的超类不在AppClassLoader中,只能委托CommonClassLoader找到它,类A和它的超类交互就会报错。开发自定义类加载器时会遇到另一种常见情况。比如在开发类隔离容器的时候,期望一些中间件会被一个不同于应用程序的独立类加载器加载,但是这时候如果中间件依赖了spring上下文,而应用程序本身也依赖于spring上下文,然后当作为springbean进行交互时,将正确报告LinkageError。SpringBoot系列教程推荐:https://github.com/javastacks/spring-boot-best-practice解决这个问题有两种方法,即控制不同类加载器加载的类不交互,或者将它们交给一个Commonparentloader进行加载。一些Tips总结了上面的错误。ClassNotFoundException和NoClassDefFoundError都是因为无法加载类导致的,而NoSuchMethodError是因为加载了错误的类导致的,而LinkageError是由于同一个类被多个类加载器加载导致的。以上所有问题都可以使用artha来检查。比如使用sc命令查看JVM加载的类信息,包括从哪个jar包读取,加载哪个类加载器。使用jad命令在jvm中查看反编译后的代码,可以定位是否存在需要的方法。并使用classloader命令查看当前所有类加载器的信息,包括加载的url,是否可以加载到指定的类或资源等。