如何解决Java.lang.NoClassDefFoundError

来源:互联网 发布:网络发票号查询 编辑:程序博客网 时间:2024/04/29 13:29

在JAVA开发中,main线程中抛出java.lang.NoClassDefFoundError是一个非常普遍且比较难解决的问题。解决这个问题的复杂性主要取决于你的软件大小和中间件部署情况,尤其要考虑在应用中出现的数量众多的classloader的情况。 

本文将从一个比较高的角度看这个问题,主要是介绍java classloader机制。 

那么,什么是java.lang.NoClassDefFoundError呢? 

我们先简单的看一下这个问题,这个runtime异常是JVM抛出的,当JVM发现一个classloader试图去Load一个class,而此class在当前的classloader tree中找不到的时候,就会抛出此异常。 

很明显,这个问题是运行期的问题,在编译期一切正常。 

那么,解决起来很简单,就是把jar包放到classpath下不就行了么? 

ok,到这里还不行,这个问题解决起来不是那么容易的,在运行期的程序classpath中加入缺少的jar包仅仅是一种解决方法。关键是,我们必须掌握此种异常的根本原因,以后解决此问题就可以以不变应万变。这就是我写这个文章的初衷。 

现在,先记住,此问题不一定是由于在classpath中缺少class的定义。 

java classloader概述 

在深入分析之前,我们必须掌握java classloader的基本原理。class loader是一个java对象,它负责load所有的class,负责查找、加载、生成一个class的基本定义信息。classloader自身采用了委托代理机制来查询class,每一个classloader的实例都有一个父classloader,所以,当一个应用的classloader去加载class A的时候,首先发生的事情是classloader委托其父classloader去加载class A,经过一串链式查找后,最终任务会落在JVM的系统启动classloader上。 

那哪里会出问题?当你期望你的应用classloader能加载class A,但是当class A被其任意一个父classloader查询到并加载,那么就可能会出现java.lang.NoClassDefFoundError。当所有的父classloader都找不到class A的时候,才会由应用自己的classloader尝试加载。


如何解决NoClassDefFoundError--第二部分 

第一部分请看:http://vipcowrie.iteye.com/blog/1561291 

本文面向的是JAVA初学者,建议你们自己编译和运行例子程序。 

本文包含了NoClassDefFoundError的原因分析和例子程序,并且给出了建议的处理策略。 

NoClassDefFoundError 问题原因1:缺少jar包 

首先最常见的原因是classpath的配置问题。例子程序: 

本例子程序尝试创建一个新的CallerClassA实例,然后执行他的一个方法,此方法引用了类ReferencingClassA,本例子演示了classpath问题导致的NoClassDefFoundError ,本例子还打印了当前的classloader chain的情况,以便进一步的分析。这个打印信息对你以后分析此类问题也很有帮助的: 

程序 

Java代码  收藏代码
  1. public class NoClassDefFoundErrorSimulator {  
  2.         public static void main(String[] args) {  
  3.   
  4.                 System.out  
  5.                                 .println("java.lang.NoClassDefFoundError Simulator");  
  6.   
  7.                 // Print current Classloader context  
  8.   
  9.                 System.out.println("\nCurrent ClassLoader chain: "  
  10.                                 + ClassloaderUtil.getCurrentClassloaderDetail());  
  11.   
  12.                 // 1. Create a new instance of CallerClassA  
  13.   
  14.                 CallerClassA caller = new CallerClassA();  
  15.   
  16.                 // 2. Execute method of the caller  
  17.   
  18.                 caller.doSomething();  
  19.   
  20.                 System.out.println("done!");  
  21.   
  22.         }  
  23. }  



Java代码  收藏代码
  1. public class CallerClassA {  
  2.         private final static String CLAZZ = CallerClassA.class.getName();  
  3.   
  4.         static {  
  5.   
  6.                 System.out.println("Classloading of " + CLAZZ + " in progress..."  
  7.                                 + ClassloaderUtil.getCurrentClassloaderDetail());  
  8.   
  9.         }  
  10.   
  11.         public CallerClassA() {  
  12.   
  13.                 System.out.println("Creating a new instance of "  
  14.                                 + CallerClassA.class.getName() + "...");  
  15.   
  16.         }  
  17.   
  18.         public void doSomething() {  
  19.   
  20.                 // Create a new instance of ReferencingClassA  
  21.   
  22.                 ReferencingClassA referencingClass = new ReferencingClassA();  
  23.   
  24.         }  
  25. }  


Java代码  收藏代码
  1. public class ReferencingClassA {  
  2.   
  3.         private final static String CLAZZ = ReferencingClassA.class.getName();  
  4.   
  5.         static {  
  6.   
  7.                 System.out.println("Classloading of " + CLAZZ + " in progress..."  
  8.                                 + ClassloaderUtil.getCurrentClassloaderDetail());  
  9.   
  10.         }  
  11.   
  12.         public ReferencingClassA() {  
  13.   
  14.                 System.out.println("Creating a new instance of "  
  15.                                 + ReferencingClassA.class.getName() + "...");  
  16.                   
  17.                 Maps.newHashMap();  
  18.   
  19.         }  
  20.   
  21.         public void doSomething() {  
  22.   
  23.                 // nothing to do...  
  24.   
  25.         }  
  26. }  


打印classloader工具类: 

Java代码  收藏代码
  1. public class ClassloaderUtil {  
  2.         public static String getCurrentClassloaderDetail() {  
  3.   
  4.                 StringBuffer classLoaderDetail = new StringBuffer();  
  5.   
  6.                 Stack<ClassLoader> classLoaderStack = new Stack<ClassLoader>();  
  7.   
  8.                 ClassLoader currentClassLoader = Thread.currentThread()  
  9.                                 .getContextClassLoader();  
  10.   
  11.                 classLoaderDetail  
  12.                                 .append("\n-----------------------------------------------------------------\n");  
  13.   
  14.                 // Build a Stack of the current ClassLoader chain  
  15.   
  16.                 while (currentClassLoader != null) {  
  17.   
  18.                         classLoaderStack.push(currentClassLoader);  
  19.   
  20.                         currentClassLoader = currentClassLoader.getParent();  
  21.   
  22.                 }  
  23.   
  24.                 // Print ClassLoader parent chain  
  25.   
  26.                 while (classLoaderStack.size() > 0) {  
  27.   
  28.                         ClassLoader classLoader = classLoaderStack.pop();  
  29.   
  30.                         // Print current  
  31.   
  32.                         classLoaderDetail.append(classLoader);  
  33.   
  34.                         if (classLoaderStack.size() > 0) {  
  35.   
  36.                                 classLoaderDetail.append("\n--- delegation ---\n");  
  37.   
  38.                         } else {  
  39.   
  40.                                 classLoaderDetail.append(" **Current ClassLoader**");  
  41.   
  42.                         }  
  43.   
  44.                 }  
  45.   
  46.                 classLoaderDetail  
  47.                                 .append("\n-----------------------------------------------------------------\n");  
  48.   
  49.                 return classLoaderDetail.toString();  
  50.   
  51.         }  
  52. }  


正常运行: 
Java代码  收藏代码
  1. java -classpath .;../guava-12.0.jar NoClassDefFoundError.NoClassDefFoundErrorSimulator  
  2. java.lang.NoClassDefFoundError Simulator  
  3.   
  4. Current ClassLoader chain:  
  5. -----------------------------------------------------------------  
  6. sun.misc.Launcher$ExtClassLoader@addbf1  
  7. --- delegation ---  
  8. sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader**  
  9. -----------------------------------------------------------------  
  10.   
  11. Classloading of NoClassDefFoundError.CallerClassA in progress...  
  12. -----------------------------------------------------------------  
  13. sun.misc.Launcher$ExtClassLoader@addbf1  
  14. --- delegation ---  
  15. sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader**  
  16. -----------------------------------------------------------------  
  17.   
  18. Creating a new instance of NoClassDefFoundError.CallerClassA...  
  19. Classloading of NoClassDefFoundError.ReferencingClassA in progress...  
  20. -----------------------------------------------------------------  
  21. sun.misc.Launcher$ExtClassLoader@addbf1  
  22. --- delegation ---  
  23. sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader**  
  24. -----------------------------------------------------------------  
  25.   
  26. Creating a new instance of NoClassDefFoundError.ReferencingClassA...  
  27. done!  



异常重现: 
Java代码  收藏代码
  1. java -classpath . NoClassDefFoundError.NoCl  
  2. java.lang.NoClassDefFoundError Simulator  
  3.   
  4. Current ClassLoader chain:  
  5. -----------------------------------------------------------------  
  6. sun.misc.Launcher$ExtClassLoader@addbf1  
  7. --- delegation ---  
  8. sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader**  
  9. -----------------------------------------------------------------  
  10.   
  11. Classloading of NoClassDefFoundError.CallerClassA in progress...  
  12. -----------------------------------------------------------------  
  13. sun.misc.Launcher$ExtClassLoader@addbf1  
  14. --- delegation ---  
  15. sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader**  
  16. -----------------------------------------------------------------  
  17.   
  18. Creating a new instance of NoClassDefFoundError.CallerClassA...  
  19. Classloading of NoClassDefFoundError.ReferencingClassA in progress...  
  20. -----------------------------------------------------------------  
  21. sun.misc.Launcher$ExtClassLoader@addbf1  
  22. --- delegation ---  
  23. sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader**  
  24. -----------------------------------------------------------------  
  25.   
  26. Creating a new instance of NoClassDefFoundError.ReferencingClassA...  
  27. Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/collect/Maps  
  28.         at NoClassDefFoundError.ReferencingClassA.<init>(ReferencingClassA.java:28)  
  29.         at NoClassDefFoundError.CallerClassA.doSomething(CallerClassA.java:31)  
  30.         at NoClassDefFoundError.NoClassDefFoundErrorSimulator.main(NoClassDefFoundErrorSimulator.jav  
  31. Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Maps  
  32.         at java.net.URLClassLoader$1.run(URLClassLoader.java:202)  
  33.         at java.security.AccessController.doPrivileged(Native Method)  
  34.         at java.net.URLClassLoader.findClass(URLClassLoader.java:190)  
  35.         at java.lang.ClassLoader.loadClass(ClassLoader.java:306)  
  36.         at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)  
  37.         at java.lang.ClassLoader.loadClass(ClassLoader.java:247)  
  38.         ... 3 more  


发生了什么?当你在classpath中不包含guava的引用的时候,由于ReferencingClassA在运行期引用了此类,导致了classloader报告找不到此类,从而出现NoClassDefFoundError。 

classloader分析 

注意: 
Java代码  收藏代码
  1. Classloading of NoClassDefFoundError.CallerClassA in progress...  
  2. -----------------------------------------------------------------  
  3. sun.misc.Launcher$ExtClassLoader@addbf1  
  4. --- delegation ---  
  5. sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader**  
  6. -------------------------  


sun.misc.Launcher$AppClassLoader是系统的classloader,负责根据classpath设置在启动的时候加载应用需要的class。 

sun.misc.Launcher$ExtClassLoader是扩展classloader,负责从java_home/lib/etc以及其他使用java.ext.dirs配置的目录从加载扩展java class。 

从打印结果可以看出,sun.misc.Launcher$ExtClassLoader是系统classloader的实际父类。 

建议处理策略 

分析异常堆栈,找到缺少的java类名称,在classpath中验证,确保编译和运行期都能找到此类。

0 0
原创粉丝点击