[英]NoClassDefFoundError on JFace FontRegistry
當我啟動SWT應用程序(通過Eclipse啟動配置文件)時,我收到以下堆棧跟蹤:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jface/resource/FontRegistry
at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:338)
at org.eclipse.jface.window.Window.close(Window.java:313)
at org.eclipse.jface.dialogs.Dialog.close(Dialog.java:971)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.close(ProgressMonitorDialog.java:348)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.finishedRun(ProgressMonitorDialog.java:582)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:498)
at com.blah.si.workflow.SWTApplication.main(SWTApplication.java:135)
現在,使這個奇怪的事情:
ClassNotFoundException
。 該類位於類路徑中。 如果我將源附加到jar,我可以調試到getFontRegistry方法。 該方法將成功執行幾次,最后在第338行拋出NoClassDefFoundError
。第337行是“if variable == null”語句,檢查是否已初始化靜態變量。 第338行正在初始化它,如果它尚未初始化。 第一次通過時,空檢查失敗,並執行初始化。 在隨后的方法傳遞中,null檢查通過,因此返回已初始化的靜態值。 在最后一次傳遞(失敗的那個)上,null檢查再次失敗(即使靜態變量已經初始化),當它嘗試重新初始化靜態變量時,拋出NoClassDefFoundError
。 這是相關的源(從第336行開始,請注意,fontRegistry是一個私有靜態變量,在其他任何地方都沒有設置): 。
public static FontRegistry getFontRegistry() {
if (fontRegistry == null) {
fontRegistry = new FontRegistry(
"org.eclipse.jface.resource.jfacefonts");
}
return fontRegistry;
}
。
由於上面#3中的特殊性,我懷疑某種奇怪的類加載器行為 - 似乎最后一次通過該方法是在另一個類加載器中?
想法?
更新: Pourquoi Litytestdata提供的答案促使我注意在ProgressMonitorDialog第458行上方的try塊中發生的事情。 實際上,該代碼拋出了一個異常,它被finally塊吞噬了。 根本原因是另一個缺少類(缺少的類不是JFontRegistry或其任何直接相關的類,而是另一個在邊緣情況下依賴於蜘蛛網的。)我正在推薦所有回答指向我注意類路徑,並接受Pourquoi的,因為這是突破。 謝謝大家。
聽起來你錯過了一個擁有依賴關系的JAR文件,正如Sanjiv JIVAN撰寫的2006年7月的博客文章所述 :
ClassNotFoundException和NoClassDefFoundError之間的區別
當ClassLoader找不到報告的類時,拋出
ClassNotFoundException
。
這通常意味着CLASSPATH
缺少該類。
它也可能意味着有問題的類試圖從另一個加載在父ClassLoader
的類ClassLoader
,因此子ClassLoader
是不可見的。
在像App Server這樣的更復雜的環境中工作時有時會出現這種情況(WebSphere對於此類ClassLoader
問題很臭名昭着)。人們經常傾向於將
java.lang.NoClassDefFoundError
與java.lang.ClassNotFoundException
混淆。 然而,有一個重要的區別。例如異常(真因為錯誤
java.lang.NoClassDefFoundError
是的一個子類java.lang.Error
)像
java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory
並不意味着ActiveMQConnectionFactory類不在
CLASSPATH
。事實上,它恰恰相反。
這意味着
ClassLoader
找到了類ActiveMQConnectionFactory
,但是在嘗試加載類時,它會在讀取類定義時遇到錯誤。當有問題的類具有靜態塊或使用
ClassLoader
未找到的ClassLoader
成員時,通常會發生這種情況。因此,要查找罪魁禍首,請查看相關類的源(本例中為
ActiveMQConnectionFactory
),並使用靜態塊或靜態成員查找代碼 。
如果您沒有訪問源代碼,那么只需使用JAD對其進行反編譯即可。在檢查代碼時,假設您找到了如下所示的代碼行,請確保
CLASSPATH
中的類SomeClass。
private static SomeClass foo = new SomeClass();
提示:要找出類所屬的jar,可以使用網站jarFinder 。 這允許您使用通配符指定類名,並在其數據庫中搜索類的jar。
jarhoo允許你做同樣的事情,但它不再免費使用。如果您想在本地路徑中找到類所屬的jar,可以使用jarscan之類的實用程序。 您只需指定要查找的類以及您希望在其中開始在jar和zip文件中搜索類的根目錄路徑。
我認為上面提到的堆棧跟蹤隱藏了真正的問題。 下面是在org.eclipse.jface.dialogs.ProgressMonitorDialog中 run
的方法中的代碼(我添加了注釋):
public void run(boolean fork, boolean cancelable,
IRunnableWithProgress runnable) throws InvocationTargetException,
InterruptedException {
setCancelable(cancelable);
try {
aboutToRun();
// Let the progress monitor know if they need to update in UI Thread
progressMonitor.forked = fork;
ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
.getDisplay());
} finally {
finishedRun(); // this is line 498
}
}
Jared的stacktrace中的第二個底線是這個類的第498行,它是finally
塊中對finishedRun()
的調用。 我懷疑真正的原因是在try
塊中拋出異常。 由於finally
塊中的代碼也會拋出異常,因此原始異常將丟失。
為了更好地處理它是否是一個類加載器問題,請查看它工作的代碼並添加:
try
{
final Class clazz;
final ClassLoader loader;
clazz = Class.forName("org/eclipse/jface/resource/FontRegistry");
loader = clazz.getClassLoader();
System.out.println("The classloader at step 1 is: " + loader);
}
catch(final Throwable ex)
{
ex.printStackTrace();
}
然后做同樣的事情,你得到NoClassDefFoundError,看看類加載器是否不同。
然后,您將能夠確保它是不同的ClassLoader。 你能報告一下這發生了什么嗎? 根據結果,我可能會有更多的想法。
添加到優秀的TofuBeer的答案 ,因為NoClassDefFoundError
指示:
ClassLoader
發現了類org.eclipse.jface.resource.FontRegistry
, ClassLoader
找不到的Class
成員 。 我們來看看org.eclipse.jface.resource.FontRegistry
源代碼 :
它沒有任何靜態變量初始化(也不是它的超類)。
我們來看看org.eclipse.jface.resource.JFaceResources
源代碼
觸發Error的getFontRegistry()
函數使用靜態變量fontRegistry :
/**
* The JFace font registry; <code>null</code> until lazily initialized or
* explicitly set.
*/
private static FontRegistry fontRegistry = null;
因此,它
引發
了一個問題 :為什么靜態初始化變量會突然被認為是null
?
因為某種方式FontRegistry
或JFaceResources
被gc卸載了 ?!
如果一個字段被聲明為static,那么無論該類最終可以創建多少個實例(可能為零),都只存在該字段的一個化身。 初始化類時,靜態字段(有時稱為類變量)會顯示(第12.4節)。
因此,無論該類的實例是否隨時存在都無關緊要,只要已經加載了Class本身,該字段就會存在。
如果這是一個eclipse插件 ,這可能與此FAQ條目有關
以下是新用戶的典型方案:
您正在編寫一個擴展插件XYZ的插件。
要使其編譯,可以從Java Build Path屬性頁或通過編輯.classpath文件向項目的構建路徑添加對插件XYZ的JAR文件的引用。
啟動運行時工作台時,會報告以下令人驚訝的錯誤:java.lang.NoClassDefFoundError: XYZ.SomeClass
。不要開始查看運行時工作台的啟動配置中的Plug-ins and Fragments選項卡。
該選項卡僅影響用於運行時工作台的插件,以及它們是從工作空間還是從Eclipse安裝目錄加載的。相反,開始查看插件清單。
編輯plugin.xml文件並確保將XYZ作為必需的插件提及 。
然后,保存plugin.xml文件。
這將自動更新項目的構建路徑。在編寫插件時,切勿手動編輯.classpath文件。
插件清單編輯器只會覆蓋您對其所做的任何更改。 不是很文明,但這就是它的工作方式。
如果您嘗試自己加載FontRegistry類(如TofoBeer所描述),您將發現如果使用FontRegistry,以下JAR的類是依賴類。
org.eclipse.core.commands_xxxxx.jar
您必須將此JAR添加到構建路徑。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.