java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java NoClassDefFoundError报错

Java中的NoClassDefFoundError报错含义解析

作者:用户bPbhIAe

这篇文章主要为大家介绍了Java中的NoClassDefFoundError含义详解,有需要的朋友可以借鉴参考下,希望能够有所帮助

引言

半夜睡得正香的时候,突然接到警告电话,于是翻起身就打卡电脑连上环境查看是什么情况?登录上之后发现有个微服务占用的句柄数量一直在持续上涨,最终导致了微服务内存溢出挂掉了。这个微服务在运行的过程中会建立SSH连接,且之前这个微服务已经遇到过很多次类似的情况了,因此第一反应是哪里建立的连接又没有关闭。

猜肯定是猜不出来的,所以第一步肯定先看下日志里面哪里在报错,然后才好对症下药。打开日志之后,经过一番排查,发现日志里面有个很奇怪的报错,日志里面有打印NoClassDefFoundError。最开始的我对这个错误的理解是不够深刻的,我的第一反应是Class文件找不到了。于是我切换到微服务的路径下,去找这个Class文件,发现文件是存在的。于是我又想,难道是文件的权限不对?我又用了ll命令看了一下文件的权限,发现文件的权限也是对的。这个时候我有点懵了,心想完了,这道题不会呀,老师没教过呀!

没办法,为了保住工作,硬着头皮还是得上。俗话说,源码之下无秘密,只有根据堆栈找到对应的源代码进行分析,看看有什么怀疑点,然后又从网上搜索了一下NoClassDefFoundError报错的含义。经过我的深思熟虑终于发现了问题的所在。

NoClassDefFoundError的报错含义

首先需要了解一下NoClassDefFoundError的报错含义,参考why-am-i-getting-a-noclassdeffounderror-in-java这个帖子:

这段话说:NoClassDefFoundError这个报错说明之前JVM尝试过去加载这个类,但是因为某些原因失败了。现在又要使用到这个类,所以又会触发这个类的加载,但是因为之前加载这个类失败了,所以这次就不会去加载这个类了,而是直接抛出NoClassDefFoundError这个报错。

解析

如果上面这段话不好理解,可以看下面这个例子,这个例子也是来自于上面那个帖子中的回答:

public class NoClassDefFoundError {
    public static void main(String[] args) {
        try {
            // 这里尝试new一个对象,会触发SimpleCalculator的第一次加载
            SimpleCalculator calculator1 = new SimpleCalculator();
        } catch (Throwable t) {
            System.out.println(t);
        }
        // 这里又尝试new一个对象,会触发SimpleCalculator的第二次加载
        SimpleCalculator calculator2 = new SimpleCalculator();
    }
​
}
​
class SimpleCalculator {
    // 类加载的时候会初始化这个类变量,这里会抛出一个运行时异常
    static int undefined = 1 / 0;
}

从上面的运行结果可以看到,在代码的第12行抛出了NoClassDefFoundError的报错,这里也是第二次尝试加载这个类的地方。第一次尝试初始化SimpleCalculator 这个类时,因为初始化会初始化 undefined 这个变量,而这个变量在初始化过程中会抛出一个异常,满足了第一次报错的条件,然后第12行尝试第二次初始化这个类,因为第一次已经初始化失败了,这个时候 JVM 就直接抛出NoClassDefFoundError 这个报错,而不是尝试再次去加载这个类。

当然实际的代码不可能会写出这么明显的Bug,我出问题的代码大概是长如下这样:

public class XXXUtils {
    private static final XXXBean bean = SpringContextUtils.getBean(XXXBean.class);
}
​
public class SpringContextUtils {
    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }
}
​
public class XXXClazz {
    public static void xxxMethod() {
        XXXUtils.xxxMethod();
    }
}
​
public class Session {
    try {
        XXX conn = xxx;
    } finnaly {
        XXXUtils.closeConn(conn);
    }
}

其中的工具类 XXXUtils 实际依赖了 SpringContexUtils 来获取 Bean,也就是依赖 Spring 上下文初始化好。但是实际在服务启动的过程中又触发了 XXXClass 的 xxxMethod 调用了 XXXUtils 的方法,这个时候就会触发 XXXUtils 的类加载,也就会触发它的 bean 变量的初始化,但是由于这个时候 Spring 上下文还没有初始化好,因此调用 SpringContextUtils.getBean() 方法就会抛出异常。在第一次初始化 XXXUtils 失败之后,等到服务正常启动,其它地方再调用 XXXUtils的方法时,就会抛出 NoClassDefFoundError 错误,导致了 XXXUtils 的所有方法都不可用,而正常的SSH连接结束之后,会调用 XXXUtils.closeConn() 方法关闭连接,当然,因为这个时候方法不可用,所以连接也关不掉,最终导致了的句柄数量不断上涨,服务也挂掉了。

以上就是Java中的NoClassDefFoundError报错解析的详细内容,更多关于Java中的NoClassDefFoundError报错解析的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文