做者:fredalxin\
地址:https://fredal.xin/classloade...java
最近在作類隔離相關的一些工做,而偏偏以前協助開發同窗時也發現會遇到許多類加載相關的異常,而且每每比較難定位與解決。這裏簡單作一個小總結。web
首先咱們來捋一捋類加載的基礎知識。面試
以上是你們比較熟悉的類加載器模型,主要包含 3 種類加載器:spring
此外咱們比較須要知道的幾點:tomcat
ClassNotFoundException 表示類找不到異常,是一種 Exception,一般發生在載入階段,當開發者主動調用 Class.forName()
、ClassLoader.loadClass()
或 ClassLoader.findSystemClass()
動態加載指定類時候,類加載器就會去 classpath 下尋找類,若是找不到就會拋出此錯誤。intellij-idea
還有另一種狀況是當一個類已經被某個類加載器加載到內存中,另一個類加載器試圖去加載時也會發生錯誤。app
ClassNotFoundException 是一個 exception 類,同時發生在主動執行動態加載時,因此咱們應該去 catch 它,防止發生一些運行時錯誤。jvm
NoClassDefFoundError 是一種和 ClassNotFoundException 很像的錯誤,只不過它是更嚴重的 error 類型。它發生在連接階段,表示 jvm 在編譯階段能夠找到相應的類,但在執行過程當中卻找不到相應的類。maven
一種緣由是因爲在編譯後運行前類被更改或者刪除了。另一種則是 classpath 自己被修改過了,這能夠經過System.getProperty("java.classpath")
來找到程序實際運行的 classpath,或者經過-classpath 命令來指定正確的 classpath。ide
那若是是在 ide 中開發,不少時候出現的狀況是咱們能夠經過 ide 編譯經過,但在實際運行的 WEB-INF/lib 下倒是沒有的。因此排查的時候咱們須要去實際的 war 包下面肯定是否有類。
咱們還會遇到 NoSuchMethodError 錯誤,它表示找不到方法,但找不到方法歸根結底是找到了不正確的類。
一般狀況下是由於 jar 包衝突問題,即加載了不匹配版本的類致使的。例如應用中有 A、B 兩個二方包,A 依賴 C-v1 包,而 B 依賴 C-v2 包,若是 maven 仲裁最後使用的是 C-v1 包,那麼當 B 加載到 C-v2 中有而 C-v1 中沒有的方法時就會報 NoSuchMethodError。
這種狀況咱們首先得知道 jvm 到底加載的是什麼版本,這可使用-verbose:class
來肯定。
LinkageError 相比較以前幾種錯誤不那麼常見,只有多個類加載器同時做用交互時纔會出現。
咱們知道 jvm 中一個類由全限定類名與類加載器肯定類實例,那麼不一樣類加載器加載的同一個類是屬於不一樣類實例的,而後在內存中若是二者發生交互,就會出現 LinkageError 異常。
通常狀況下,jvm 加載類都會遵循以前所述的雙親委派原則,不太可能出現一個類有不一樣類加載器加載的狀況。但在諸如 tomcat 之類的 javaEE 環境中,經常出這種情況,這是因爲 tomcat 上的 web 應用類加載機制稍有不一樣,每一個資源模塊(好比一個 war 包)都優先使用自身的資源,突破了雙親委派模型:
當 appClassLoader 加載類時候,會首先在本身的本地資源庫中查找類,其次纔會走雙親委派模型。那麼若是一個類 A 由 AppClassLoaderx 加載,但其超類在 AppClassLoader 中沒有,只有委託 CommonClassLoader 才能找到,當類 A 與其超類進行交互時就會報錯了。
還有一種比較常見的狀況是進行自定義類加載器開發時遇到。好比開發類隔離容器時,指望將某些中間件都由與應用不一樣的獨立類加載器加載,但這時候若是中間件依賴 spring context,而應用自己也依賴 spring context,那麼 做爲 spring bean 交互時候就會妥妥報 LinkageError 了。
解決這個問題的辦法包括 2 種,即控制不一樣類加載器加載的類不進行交互,或者都交於一個共同的父加載器進行加載。
總結一下以上幾種錯誤。ClassNotFoundException 以及 NoClassDefFoundError 都是因爲加載不到類致使的,而 NoSuchMethodError 是由於加載了不正確的類,LinkageError 則是因爲同一個類被多個類加載器加載所致使的。
以上這些問題均可以使用arthas進行排查。例如使用 sc 命令來查看 JVM 已加載的類信息,包括從哪一個 jar 包讀取,由哪一個類加載器加載。使用 jad 命令來查看 jvm 中反編譯的代碼,能夠定位到底到底有沒有所需 method。以及使用 classloader 命令,來查看當前全部 classloader 的信息,包括加載的 urls,是否能加載到指定的類或者 resources 等。
近期熱文推薦:
1.600+ 道 Java面試題及答案整理(2021最新版)
2.終於靠開源項目弄到 IntelliJ IDEA 激活碼了,真香!
3.阿里 Mock 工具正式開源,幹掉市面上全部 Mock 工具!
4.Spring Cloud 2020.0.0 正式發佈,全新顛覆性版本!
以爲不錯,別忘了隨手點贊+轉發哦!