Java中的NoClassDefFoundError報錯含義解析
引言
半夜睡得正香的時候,突然接到警告電話,于是翻起身就打卡電腦連上環(huán)境查看是什么情況?登錄上之后發(fā)現(xiàn)有個微服務占用的句柄數(shù)量一直在持續(xù)上漲,最終導致了微服務內(nèi)存溢出掛掉了。這個微服務在運行的過程中會建立SSH
連接,且之前這個微服務已經(jīng)遇到過很多次類似的情況了,因此第一反應是哪里建立的連接又沒有關閉。
猜肯定是猜不出來的,所以第一步肯定先看下日志里面哪里在報錯,然后才好對癥下藥。打開日志之后,經(jīng)過一番排查,發(fā)現(xiàn)日志里面有個很奇怪的報錯,日志里面有打印NoClassDefFoundError
。最開始的我對這個錯誤的理解是不夠深刻的,我的第一反應是Class
文件找不到了。于是我切換到微服務的路徑下,去找這個Class
文件,發(fā)現(xiàn)文件是存在的。于是我又想,難道是文件的權限不對?我又用了ll
命令看了一下文件的權限,發(fā)現(xiàn)文件的權限也是對的。這個時候我有點懵了,心想完了,這道題不會呀,老師沒教過呀!
沒辦法,為了保住工作,硬著頭皮還是得上。俗話說,源碼之下無秘密,只有根據(jù)堆棧找到對應的源代碼進行分析,看看有什么懷疑點,然后又從網(wǎng)上搜索了一下NoClassDefFoundError
報錯的含義。經(jīng)過我的深思熟慮終于發(fā)現(xiàn)了問題的所在。
NoClassDefFoundError的報錯含義
首先需要了解一下NoClassDefFoundError
的報錯含義,參考why-am-i-getting-a-noclassdeffounderror-in-java這個帖子:
這段話說:NoClassDefFoundError這個報錯說明之前JVM嘗試過去加載這個類,但是因為某些原因失敗了?,F(xiàn)在又要使用到這個類,所以又會觸發(fā)這個類的加載,但是因為之前加載這個類失敗了,所以這次就不會去加載這個類了,而是直接拋出NoClassDefFoundError這個報錯。
解析
如果上面這段話不好理解,可以看下面這個例子,這個例子也是來自于上面那個帖子中的回答:
public class NoClassDefFoundError { public static void main(String[] args) { try { // 這里嘗試new一個對象,會觸發(fā)SimpleCalculator的第一次加載 SimpleCalculator calculator1 = new SimpleCalculator(); } catch (Throwable t) { System.out.println(t); } // 這里又嘗試new一個對象,會觸發(fā)SimpleCalculator的第二次加載 SimpleCalculator calculator2 = new SimpleCalculator(); } ? } ? class SimpleCalculator { // 類加載的時候會初始化這個類變量,這里會拋出一個運行時異常 static int undefined = 1 / 0; }
從上面的運行結(jié)果可以看到,在代碼的第12行拋出了NoClassDefFoundError
的報錯,這里也是第二次嘗試加載這個類的地方。第一次嘗試初始化SimpleCalculator
這個類時,因為初始化會初始化 undefined
這個變量,而這個變量在初始化過程中會拋出一個異常,滿足了第一次報錯的條件,然后第12行嘗試第二次初始化這個類,因為第一次已經(jīng)初始化失敗了,這個時候 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
上下文初始化好。但是實際在服務啟動的過程中又觸發(fā)了 XXXClass
的 xxxMethod
調(diào)用了 XXXUtils
的方法,這個時候就會觸發(fā) XXXUtils
的類加載,也就會觸發(fā)它的 bean
變量的初始化,但是由于這個時候 Spring
上下文還沒有初始化好,因此調(diào)用 SpringContextUtils.getBean()
方法就會拋出異常。在第一次初始化 XXXUtils
失敗之后,等到服務正常啟動,其它地方再調(diào)用 XXXUtils
的方法時,就會拋出 NoClassDefFoundError
錯誤,導致了 XXXUtils
的所有方法都不可用,而正常的SSH連接結(jié)束之后,會調(diào)用 XXXUtils.closeConn()
方法關閉連接,當然,因為這個時候方法不可用,所以連接也關不掉,最終導致了的句柄數(shù)量不斷上漲,服務也掛掉了。
以上就是Java中的NoClassDefFoundError報錯解析的詳細內(nèi)容,更多關于Java中的NoClassDefFoundError報錯解析的資料請關注腳本之家其它相關文章!
相關文章
Spring的連接數(shù)據(jù)庫以及JDBC模板(實例講解)
下面小編就為大家?guī)硪黄猄pring的連接數(shù)據(jù)庫以及JDBC模板(實例講解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10springboot應用服務啟動事件的監(jiān)聽實現(xiàn)
本文主要介紹了springboot應用服務啟動事件的監(jiān)聽實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-04-04Spring的Bean生命周期之BeanDefinition詳解
這篇文章主要介紹了Spring的Bean生命周期之BeanDefinition詳解,在spring bean創(chuàng)建過程 依賴 BeanDefinition 中的信息處理bean的生產(chǎn),BeanDefinition 是 Spring Framework 中定義 Bean 的配置元信息接口,需要的朋友可以參考下2023-12-12mybatis多個區(qū)間處理方式(雙foreach循環(huán))
這篇文章主要介紹了mybatis多個區(qū)間處理方式(雙foreach循環(huán)),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02詳解IDEA用maven創(chuàng)建springMVC項目和配置
本篇文章主要介紹了詳解IDEA用maven創(chuàng)建springMVC項目和配置 ,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09