`

java.lang.NoClassDefFoundError

    博客分类:
  • J2EE
 
阅读更多
在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 问题原因1:缺少jar包

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

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

程序
Java代码 
public class NoClassDefFoundErrorSimulator { 
        public static void main(String[] args) { 
 
                System.out 
                                .println("java.lang.NoClassDefFoundError Simulator"); 
 
                // Print current Classloader context 
 
                System.out.println("\nCurrent ClassLoader chain: " 
                                + ClassloaderUtil.getCurrentClassloaderDetail()); 
 
                // 1. Create a new instance of CallerClassA 
 
                CallerClassA caller = new CallerClassA(); 
 
                // 2. Execute method of the caller 
 
                caller.doSomething(); 
 
                System.out.println("done!"); 
 
        } 




Java代码 
public class CallerClassA { 
        private final static String CLAZZ = CallerClassA.class.getName(); 
 
        static { 
 
                System.out.println("Classloading of " + CLAZZ + " in progress..." 
                                + ClassloaderUtil.getCurrentClassloaderDetail()); 
 
        } 
 
        public CallerClassA() { 
 
                System.out.println("Creating a new instance of " 
                                + CallerClassA.class.getName() + "..."); 
 
        } 
 
        public void doSomething() { 
 
                // Create a new instance of ReferencingClassA 
 
                ReferencingClassA referencingClass = new ReferencingClassA(); 
 
        } 



Java代码 
public class ReferencingClassA { 
 
        private final static String CLAZZ = ReferencingClassA.class.getName(); 
 
        static { 
 
                System.out.println("Classloading of " + CLAZZ + " in progress..." 
                                + ClassloaderUtil.getCurrentClassloaderDetail()); 
 
        } 
 
        public ReferencingClassA() { 
 
                System.out.println("Creating a new instance of " 
                                + ReferencingClassA.class.getName() + "..."); 
                 
                Maps.newHashMap(); 
 
        } 
 
        public void doSomething() { 
 
                // nothing to do... 
 
        } 



打印classloader工具类:

Java代码 
public class ClassloaderUtil { 
        public static String getCurrentClassloaderDetail() { 
 
                StringBuffer classLoaderDetail = new StringBuffer(); 
 
                Stack<ClassLoader> classLoaderStack = new Stack<ClassLoader>(); 
 
                ClassLoader currentClassLoader = Thread.currentThread() 
                                .getContextClassLoader(); 
 
                classLoaderDetail 
                                .append("\n-----------------------------------------------------------------\n"); 
 
                // Build a Stack of the current ClassLoader chain 
 
                while (currentClassLoader != null) { 
 
                        classLoaderStack.push(currentClassLoader); 
 
                        currentClassLoader = currentClassLoader.getParent(); 
 
                } 
 
                // Print ClassLoader parent chain 
 
                while (classLoaderStack.size() > 0) { 
 
                        ClassLoader classLoader = classLoaderStack.pop(); 
 
                        // Print current 
 
                        classLoaderDetail.append(classLoader); 
 
                        if (classLoaderStack.size() > 0) { 
 
                                classLoaderDetail.append("\n--- delegation ---\n"); 
 
                        } else { 
 
                                classLoaderDetail.append(" **Current ClassLoader**"); 
 
                        } 
 
                } 
 
                classLoaderDetail 
                                .append("\n-----------------------------------------------------------------\n"); 
 
                return classLoaderDetail.toString(); 
 
        } 



正常运行:
Java代码 
java -classpath .;../guava-12.0.jar NoClassDefFoundError.NoClassDefFoundErrorSimulator 
java.lang.NoClassDefFoundError Simulator 
 
Current ClassLoader chain: 
----------------------------------------------------------------- 
sun.misc.Launcher$ExtClassLoader@addbf1 
--- delegation --- 
sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader** 
----------------------------------------------------------------- 
 
Classloading of NoClassDefFoundError.CallerClassA in progress... 
----------------------------------------------------------------- 
sun.misc.Launcher$ExtClassLoader@addbf1 
--- delegation --- 
sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader** 
----------------------------------------------------------------- 
 
Creating a new instance of NoClassDefFoundError.CallerClassA... 
Classloading of NoClassDefFoundError.ReferencingClassA in progress... 
----------------------------------------------------------------- 
sun.misc.Launcher$ExtClassLoader@addbf1 
--- delegation --- 
sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader** 
----------------------------------------------------------------- 
 
Creating a new instance of NoClassDefFoundError.ReferencingClassA... 
done! 



异常重现:
Java代码 
java -classpath . NoClassDefFoundError.NoCl 
java.lang.NoClassDefFoundError Simulator 
 
Current ClassLoader chain: 
----------------------------------------------------------------- 
sun.misc.Launcher$ExtClassLoader@addbf1 
--- delegation --- 
sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader** 
----------------------------------------------------------------- 
 
Classloading of NoClassDefFoundError.CallerClassA in progress... 
----------------------------------------------------------------- 
sun.misc.Launcher$ExtClassLoader@addbf1 
--- delegation --- 
sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader** 
----------------------------------------------------------------- 
 
Creating a new instance of NoClassDefFoundError.CallerClassA... 
Classloading of NoClassDefFoundError.ReferencingClassA in progress... 
----------------------------------------------------------------- 
sun.misc.Launcher$ExtClassLoader@addbf1 
--- delegation --- 
sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader** 
----------------------------------------------------------------- 
 
Creating a new instance of NoClassDefFoundError.ReferencingClassA... 
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/common/collect/Maps 
        at NoClassDefFoundError.ReferencingClassA.<init>(ReferencingClassA.java:28) 
        at NoClassDefFoundError.CallerClassA.doSomething(CallerClassA.java:31) 
        at NoClassDefFoundError.NoClassDefFoundErrorSimulator.main(NoClassDefFoundErrorSimulator.jav 
Caused by: java.lang.ClassNotFoundException: com.google.common.collect.Maps 
        at java.net.URLClassLoader$1.run(URLClassLoader.java:202) 
        at java.security.AccessController.doPrivileged(Native Method) 
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190) 
        at java.lang.ClassLoader.loadClass(ClassLoader.java:306) 
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301) 
        at java.lang.ClassLoader.loadClass(ClassLoader.java:247) 
        ... 3 more 


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

classloader分析

注意:
Java代码 
Classloading of NoClassDefFoundError.CallerClassA in progress... 
----------------------------------------------------------------- 
sun.misc.Launcher$ExtClassLoader@addbf1 
--- delegation --- 
sun.misc.Launcher$AppClassLoader@19821f **Current ClassLoader** 
------------------------- 


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中验证,确保编译和运行期都能找到此类。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics