ClassNotFoundException,NoClassDefFoundError,NoSuchMethodError排查

前言

在使用java開發(fā)的過程中時(shí)常會(huì)碰到以上三個(gè)錯(cuò)誤,其中NoClassDefFoundError、NoSuchMethodError兩個(gè)error遭遇得會(huì)多一些。本文會(huì)簡(jiǎn)單分析三個(gè)異常發(fā)生的原因,并給出排查思路和相關(guān)工具。

定義

NoClassDefFoundError

Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.
The searched-for class definition existed when the currently executing class was compiled, but the definition can no longer be found.

ClassNotFoundException

Thrown when an application tries to load in a class through its string name using: The forName method in class Class. The findSystemClass method in class ClassLoader . The loadClass method in class ClassLoader.

這兩者都發(fā)生在運(yùn)行期‘找不到需要的類’,但是需要注意的是ClassNotFoundException出現(xiàn)主要是由于在運(yùn)行期嘗試根據(jù)類名加載(通過Class.forName、ClassLoader.findSystemClass、ClassLoader.loadClass),但是找不到需要加載的類。ClassNotFoundException的異常場(chǎng)景有限,所以通常遭遇得比較少,如果遭遇了只要查找classpath是是否真的存在對(duì)應(yīng)的類即可。平時(shí)遭遇的更常見的與‘找不到類’相關(guān)的錯(cuò)誤是NoClassDefFoundError

NoClassDefFoundError

這個(gè)錯(cuò)誤發(fā)生的場(chǎng)景就比較多了,較為常見的有:

  • 運(yùn)行期真真找不到對(duì)應(yīng)的類

例如A.jar的A.class依賴了B.jar的B.class,但是B.jar中由于某些原因并不存在B.class,此時(shí)就會(huì)拋出NoClassDefFoundError

  • 加載的類初始化錯(cuò)誤

加載的class在初始化(loaded->linked->initialized)過程中出錯(cuò)了,初始化過程不可逆,以后凡是使用該class的地方都會(huì)拋出NoClassDefFoundError。這個(gè)錯(cuò)誤通常是發(fā)生在clinit方法中,具體可能是靜態(tài)變量,靜態(tài)代碼塊。可參考寒泉子大大寫的不可逆的類初始化過程 。通常錯(cuò)誤堆棧表現(xiàn)為:


Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class xxxx

...

如果你運(yùn)氣好的話,可能在該錯(cuò)誤的上方看到j(luò)ava.lang.ExceptionInInitializerError的錯(cuò)誤堆棧從而找到對(duì)應(yīng)出錯(cuò)的地方;如果運(yùn)氣不好的話,該異??赡鼙煌痰?如果出問題的類你有權(quán)限修改的話,你可以顯式catch異常打印日志,如果是二方包或者三方包的話...可能要使用比較tricky的手段了。

NoSuchMethodError

這個(gè)比較好理解了,在運(yùn)行時(shí)找不到對(duì)應(yīng)的類的對(duì)應(yīng)方法,通常由于jar包依賴沖突導(dǎo)致。

排查思路與工具

ClassNotFoundException

由于是出現(xiàn)在運(yùn)行期,我們要確定是否真的不存在該類,推薦使用腳本掃描war包(重點(diǎn)找lib包),此掃描腳本 來(lái)自于阿里的哲良大大。


sh /home/admin/devops/find-in-jars.sh -d . 'xxx.class'

如果沒有找到,則加入對(duì)應(yīng)的相關(guān)jar包即可

NoClassDefFoundError

首先看對(duì)應(yīng)的錯(cuò)誤堆棧,如果錯(cuò)誤堆棧類似于:


Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class xxxx

...

則表明是類的初始化過程發(fā)生了不可逆錯(cuò)誤,參考上文所說的解決方案。

如果堆棧類似于:


java.lang.NoClassDefFoundError: com/taobao/pamirs/base/log/ErrorMonitorLog

沒有出現(xiàn)‘ Could not initialize class’等關(guān)鍵字,此時(shí)可參考ClassNotFoundException的排查方法,使用掃描腳本確定war包是否真的存在該類,不存在的話則添加相關(guān)的jar包。

NoSuchMethodError

這個(gè)錯(cuò)誤多半是由于jar包依賴沖突導(dǎo)致,依賴沖突是一個(gè)非常DT的問題。NoSuchMethodError出現(xiàn)是多半是存在兩個(gè)同fully qualified name的class,剛好優(yōu)先加載到了少了方法的那個(gè)。更為DT的是可能在不同機(jī)器上表現(xiàn)不一致(首先加載哪個(gè)class順序不確定),‘對(duì)于classloader而言,找文件的過程取決于文件系統(tǒng)返回的順序,簡(jiǎn)單的說,在linux上取決于兩個(gè)inode的順序’。

上面有些扯遠(yuǎn)了...解決這個(gè)問題先找到錯(cuò)誤堆棧:


NoSuchMethodError: com.foo.SomeService.doSmth()Z

通過掃描腳本掃描lib包,看是否存在兩個(gè)同fully qualified name的class出現(xiàn)在兩個(gè)不同的jar版,如果存在,則排除其中一個(gè)版本的jar包。

但是需要注意的是,也有一種可能性是掃描com.foo.SomeService 會(huì)發(fā)現(xiàn)只存在一個(gè)對(duì)應(yīng)class name的class文件。這時(shí)候需要調(diào)整下思路了,很可能是其父類類加載沖突了。比如說曾經(jīng)遭遇過org.apache.log4j.DailyRollingFileAppender.setAppend NoSuchMethodError,排查半天后發(fā)現(xiàn)是其父類org.apache.log4j.FileAppender類加載沖突了。

Maven依賴樹

由于集團(tuán)多使用maven,由于依賴沖突會(huì)導(dǎo)致如上所述的NoSuchMethodError錯(cuò)誤,在遭遇此類問題時(shí)通常會(huì)需要打印出應(yīng)用的maven依賴樹,通過我們會(huì)使用以下幾種方式:

  • maven命令

mvn dependency:tree -Dverbose > tree.txt

當(dāng)然加不加verbose也行

使用‘Dependency Analyzer’面板

  • 不知道為啥我的IDEA從某個(gè)版本開始就沒法使用maven helper了,參考了其部分代碼,自己寫了個(gè)簡(jiǎn)易插件來(lái)生成maven的依賴樹。使用方法是,在對(duì)應(yīng)的pom文件右鍵選擇maven菜單中的tree子菜單即可生成tree.txt文件,這個(gè)操作等同于在pom文件所在的子module里mvn dependency:tree -Dverbose > tree.txt。此插件源碼在此,里面也附上了可安裝的jar,下載安裝即可使用。

總結(jié)

本文簡(jiǎn)述了ClassNotFoundException,NoClassDefFoundError,NoSuchMethodError的發(fā)生場(chǎng)景,給出了相關(guān)的排查思路與排查工具。

參考文獻(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,545評(píng)論 19 139
  • Jar包沖突是老生常談的問題,幾乎每一個(gè)Java程序猿都不可避免地遇到過,并且也都能想到通常的原因一般是同一個(gè)Ja...
    sherlockyb閱讀 37,825評(píng)論 1 65
  • 1 基本信息 每個(gè)開發(fā)人員對(duì)java.lang.ClassNotFoundExcetpion這個(gè)異常肯定都不陌生,...
    java小菜鳥閱讀 2,687評(píng)論 0 15
  • 咦,怎么好像有人在不斷搖晃著我的肩膀?緊接著,就聽見旁邊的妻子嗔怪著說到:“老伴兒,你醒醒,睡覺不好好睡覺,樂什么...
    天馬行空我也閱讀 742評(píng)論 0 1
  • 大年三十兒,婆婆病重,住進(jìn)急診搶救室。醫(yī)院門診大廳已經(jīng)空空蕩蕩,相鄰的急診區(qū)除了幾名值班的醫(yī)護(hù)人員,也只有我們和另...
    愛做美夢(mèng)的懶貓閱讀 457評(píng)論 3 15

友情鏈接更多精彩內(nèi)容