今天在排查一個線上的問題,線上的一個應用在初始化一個類的靜態字段的時候出現了 NoClassDefFoundError
,而且在致使 NoClassDefFoundError
出現的根本緣由消失後,後續再次嘗試初始化這個類的時候,持續出現了 NoClassDefFoundError
。java
因而懷疑 JVM 是否是對一個類的 NoClassDefFoundError
作了緩存,在第一次加載這個類出現 NoClassDefFoundError
之後,後續再嘗試加載就直接拋出 NoClassDefFoundError
。緩存
爲了證明本身的猜測,嘗試設計了一個簡單的實驗,一個涉及三個類oop
public class Test1 { static Test2 test2 = new Test2(); }
public class Test2 { }
public class Test { public static void main(String... args) throws Exception { while(true) { System.out.println("================================"); try { new Test1(); // 嘗試實例化 Test1,觸發 NoClassDefFoundError } catch (Throwable e) { e.printStackTrace(); try { Test.class.getClassLoader().loadClass("Test2"); // 嘗試加載 Test2,用於證明當將 // Test2.class 拷貝到 ClassPath 下的時候, // Test2 就能夠加載到了。 } catch (Throwable ex) { ex.printStackTrace(); } } Thread.sleep(3000); } } }
上述類的的做用是:Test2 是一個空的類,Test1 裏面有一個 Test2 的靜態成員。Test 是程序的主入口,在一個無限循環內部,不斷地嘗試去實例化 Test1,而且在加載 Test1 出現異常的時候,嘗試加載一下 Test2。this
實驗的步驟是:spa
javac Test.java
java Test
,這個時候程序去加載 Test1 的時候,就會出現 NoClassDefFoundError
,而且在嘗試加載 Test2 的時候,會出現 ClassNotFoundException
。NoClassDefFoundError
,在加載 Test2 的時候,不會出現 ClassNotFoundException
。實驗的第二步的目的是爲了程序在加載 Test1 的時候由於找不到 Test2 出現 NoClassDefFoundError
,第四步是爲了和第二步作對照,說明在後續程序能夠加載到 Test2 的時候,在實例化 Test1 的時候,依舊出現 NoClassDefFoundError
設計
在個人機器上,按照上面的方式去操做,結果以下:code
結果正如預期,即便在後面 Test2 在 ClassPath 下的時候,NoClassDefFoundError
依舊出現,因此 JVM 裏面確定有地方對 NoClassDefFoundError
作了緩存。get
帶着這個疑問,請教了部門裏面的 JVM 專家,這個猜想獲得了證明,而且他給出了 JVM 內部具體處理這段邏輯的代碼,處理的代碼在 JDK 的 instanceKlass.cpp
這個文件裏面:it
bool instanceKlass::link_class_impl( instanceKlassHandle this_oop, bool throw_verifyerror, TRAPS) { // check for error state if (this_oop->is_in_error_state()) { ResourceMark rm(THREAD); THROW_MSG_(vmSymbols::java_lang_NoClassDefFoundError(), this_oop->external_name(), false); } // return if already verified if (this_oop->is_linked()) { return true; }
而且在 instanceClass.hpp
這個文件中,定義了類的 _init_state
,其中,is_in_error_state
這個方法的定義以下:io
bool is_in_error_state() const { return _init_state == initialization_error; }