簡體   English   中英

為什么我在 Java 中收到 NoClassDefFoundError?

[英]Why am I getting a NoClassDefFoundError in Java?

我在運行 Java 應用程序時收到NoClassDefFoundError 這通常是什么原因造成的?

雖然這可能是由於編譯時和運行時之間的類路徑不匹配,但這不一定是真的。

在這種情況下,將兩個或三個不同的例外直接放在我們的腦海中是很重要的:

  1. java.lang.ClassNotFoundException此異常表示在類路徑中找不到該類。 這表明我們正在嘗試加載類定義,並且類路徑中不存在該類。

  2. java.lang.NoClassDefFoundError該異常表示JVM在其內部類定義數據結構中查找類的定義,但沒有找到。 這與說它無法從類路徑加載不同。 通常這表明我們之前嘗試從類路徑加載一個類,但由於某種原因它失敗了 - 現在我們試圖再次使用該類(因此需要加載它,因為它上次失敗了),但是我們'甚至不會嘗試加載它,因為我們之前未能加載它(並且有理由懷疑我們會再次失敗)。 較早的失敗可能是 ClassNotFoundException 或 ExceptionInInitializerError(指示靜態初始化塊中的失敗)或任何數量的其他問題。 關鍵是,NoClassDefFoundError 不一定是類路徑問題。

當您的代碼依賴於一個類文件並且它在編譯時存在但在運行時找不到時會導致這種情況。 尋找構建時間和運行時類路徑的差異。

這是說明java.lang.NoClassDefFoundError的代碼。 有關詳細說明,請參閱Jared 的回答

NoClassDefFoundErrorDemo.java

public class NoClassDefFoundErrorDemo {
    public static void main(String[] args) {
        try {
            // The following line would throw ExceptionInInitializerError
            SimpleCalculator calculator1 = new SimpleCalculator();
        } catch (Throwable t) {
            System.out.println(t);
        }
        // The following line would cause NoClassDefFoundError
        SimpleCalculator calculator2 = new SimpleCalculator();
    }

}

簡單計算器.java

public class SimpleCalculator {
    static int undefined = 1 / 0;
}

Java中的NoClassDefFoundError

定義:

  1. Java 虛擬機無法在運行時找到在編譯時可用的特定類。

  2. 如果一個類在編譯時存在,但在運行時在 java 類路徑中不可用。

在此處輸入圖像描述

例子:

  1. 該類不在 Classpath 中,沒有確切的方法可以知道它,但很多時候你可以看看打印 System.getproperty("java.classpath") 它會從那里打印你至少可以得到的類路徑您的實際運行時類路徑的想法。
  2. NoClassDefFoundError 的一個簡單示例是類屬於缺少的 JAR 文件或 JAR 未添加到類路徑中,或者有時 jar 的名稱已被某些人更改,例如我的一位同事已將 tibco.jar 更改為 tibco_v3.jar 並且程序是java.lang.NoClassDefFoundError 失敗,我想知道出了什么問題。

  3. 只需嘗試使用您認為可以工作的類路徑顯式運行 -classpath 選項,如果它正在工作,那么這是一個肯定的簡短信號,表明有人正在覆蓋 java 類路徑。

  4. JAR 文件的權限問題也可能導致 Java 中的 NoClassDefFoundError。
  5. XML 配置上的錯字也可能導致 Java 中的 NoClassDefFoundError。
  6. 當您在包中定義的編譯類在加載時不存在於同一個包中時,就像在 JApplet 的情況下一樣,它將在 Java 中拋出 NoClassDefFoundError。

可能的解決方案:

  1. 該類在 Java 類路徑中不可用。
  2. 如果您在 J2EE 環境中工作,多個 Classloader 之間的 Class 的可見性也會導致 java.lang.NoClassDefFoundError,請參閱示例和場景部分進行詳細討論。
  3. 檢查日志文件中的 java.lang.ExceptionInInitializerError。 由於靜態初始化失敗導致的 NoClassDefFoundError 很常見。
  4. 因為 NoClassDefFoundError 是 java.lang.LinkageError 的子類,所以如果其中一個依賴項(如本機庫)可能不可用,它也會出現。
  5. 任何啟動腳本都會覆蓋 Classpath 環境變量。
  6. 您可能正在使用 jar 命令運行程序,並且清單文件的 ClassPath 屬性中未定義類。

資源:

解決 NoClassDefFoundError 的 3 種方法

java.lang.NoClassDefFoundError 問題模式

我發現,當使用運行時發現的不兼容版本的類編譯代碼時,有時會出現 NoClassDefFound 錯誤。 我記得的具體實例是 apache 軸庫。 在我的運行時類路徑中實際上有 2 個版本,它選擇了過期和不兼容的版本,而不是正確的版本,導致 NoClassDefFound 錯誤。 這是在一個命令行應用程序中,我正在使用與此類似的命令。

set classpath=%classpath%;axis.jar

我能夠通過使用以下方法獲取正確的版本:

set classpath=axis.jar;%classpath%;

這是迄今為止我找到的最好的解決方案

假設我們有一個名為org.mypackage的包,其中包含以下類:

  • HelloWorld(主類)
  • 支持類
  • 實用程序類

並且定義此包的文件物理存儲在目錄D:\myprogram (在 Windows 上)或/home/user/myprogram (在 Linux 上)下。

文件結構將如下所示: 在此處輸入圖像描述

當我們調用 Java 時,我們指定要運行的應用程序的名稱: org.mypackage.HelloWorld 然而,我們還必須告訴 Java 在哪里尋找定義我們包的文件和目錄。 因此,要啟動程序,我們必須使用以下命令: 在此處輸入圖像描述

您可能會看到很多NoClassDefFoundErrors的一個有趣案例是:

  1. 在類Examplestatic塊中throw RuntimeException
  2. 攔截它(或者如果它只是像在測試用例中拋出一樣無關緊要)
  3. 嘗試創建此類Example的實例

static class Example {
    static {
        thisThrowsRuntimeException();
    }
}

static class OuterClazz {

    OuterClazz() {
        try {
            new Example();
        } catch (Throwable ignored) { //simulating catching RuntimeException from static block
            // DO NOT DO THIS IN PRODUCTION CODE, THIS IS JUST AN EXAMPLE in StackOverflow
        }

        new Example(); //this throws NoClassDefFoundError
    }
}

NoClassDefError將與靜態塊RuntimeException中的ExceptionInInitializerError一起拋出。


當您在UNIT TESTS中看到NoClassDefFoundErrors時,這一點尤其重要。

在某種程度上,您在測試之間“共享” static塊執行,但初始ExceptionInInitializerError將僅在一個測試用例中。 第一個使用有問題的Example類。 其他使用Example類的測試用例只會拋出NoClassDefFoundErrors

我在Maven中使用Spring Framework並在我的項目中解決了這個錯誤。

類中存在運行時錯誤。 我正在將屬性讀取為整數,但是當它從屬性文件中讀取值時,它的值是雙倍的。

Spring 沒有給我完整的堆棧跟蹤運行時在哪一行失敗。 它只是說NoClassDefFoundError 但是當我將它作為本機 Java 應用程序執行時(將其從 MVC 中取出),它給出了ExceptionInInitializerError ,這是真正的原因,也是我跟蹤錯誤的方式。

@xli 的回答讓我深入了解了我的代碼中可能存在的問題。

當運行時類加載器加載的類無法訪問 java 根加載器已經加載的類時,我得到 NoClassFoundError。 因為不同的類加載器位於不同的安全域中(根據 java),jvm 不允許在運行時加載器地址空間中解析已經由 rootloader 加載的類。

使用“java -javaagent:tracer.jar [YOUR java ARGS]”運行程序

它生成顯示加載的類的輸出,以及加載該類的加載器環境。 跟蹤類無法解析的原因非常有幫助。

// ClassLoaderTracer.java
// From: https://blogs.oracle.com/sundararajan/entry/tracing_class_loading_1_5

import java.lang.instrument.*;
import java.security.*;

// manifest.mf
// Premain-Class: ClassLoadTracer

// jar -cvfm tracer.jar manifest.mf ClassLoaderTracer.class

// java -javaagent:tracer.jar  [...]

public class ClassLoadTracer 
{
    public static void premain(String agentArgs, Instrumentation inst) 
    {
        final java.io.PrintStream out = System.out;
        inst.addTransformer(new ClassFileTransformer() {
            public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

                String pd = (null == protectionDomain) ? "null" : protectionDomain.getCodeSource().toString();
                out.println(className + " loaded by " + loader + " at " + new java.util.Date() + " in " + pd);

                // dump stack trace of the thread loading class 
                Thread.dumpStack();

                // we just want the original .class bytes to be loaded!
                // we are not instrumenting it...
                return null;
            }
        });
    }
}

下面的技術幫助了我很多次:

System.out.println(TheNoDefFoundClass.class.getProtectionDomain().getCodeSource().getLocation());

其中 TheNoDefFoundClass 是由於偏愛程序使用的同一庫的舊版本而可能“丟失”的類。 這最常發生在這樣的情況下,當客戶端軟件被部署到一個占主導地位的容器中時,它配備了自己的類加載器和大量最流行的庫的古老版本。

如果您有生成代碼(EMF 等),則可能有太多靜態初始化程序會占用所有堆棧空間。

請參閱堆棧溢出問題如何增加 Java 堆棧大小? .

ClassNotFoundException 與 NoClassDefFoundError

[類加載器]

靜態與動態類加載

Static(Implicit) class loading - 引用、實例化或繼承的結果。

MyClass myClass = new MyClass();

Dynamic(Explicit) class loading是 Class.forName()、loadClass()、findSystemClass() 的結果

MyClass myClass = (MyClass) Class.forName("MyClass").newInstance();

每個類都有一個使用loadClass(String name);ClassLoader 這就是為什么

explicit class loader uses implicit class loader

NoClassDefFoundErrorexplicit class loader的一部分。 保證在編譯過程中出現了這個類但現在(在運行時)它不存在是Error的。

ClassNotFoundExceptionimplicit class loader的一部分。 在另外可以使用它的情況下具有彈性是Exception - 例如反射。

同一項目的兩個不同的結帳副本

就我而言,問題在於 Eclipse 無法區分同一項目的兩個不同副本。 我有一個鎖定在主干上(SVN 版本控制),另一個在一個分支中工作。 我在工作副本中嘗試了一個更改作為 JUnit 測試用例,其中包括將私有內部類提取為自己的公共類,當它工作時,我打開項目的另一個副本以查看其他的需要更改的部分代碼。 在某些時候,彈出NoClassDefFoundError抱怨私有內部類不存在; 雙擊堆棧跟蹤將我帶到錯誤項目副本中的源文件。

關閉項目的主干副本並再次運行測試用例即可解決問題。

我通過禁用所有模塊的 preDexLibraries 解決了我的問題:

dexOptions {
        preDexLibraries false
        ...

NoClassDefFoundError也可能在靜態初始化程序嘗試加載在運行時不可用的資源包時發生,例如受影響的類嘗試從META-INF目錄加載但不存在的屬性文件。 如果您沒有捕獲NoClassDefFoundError ,有時您將無法看到完整的堆棧跟蹤; 為了克服這個問題,您可以暫時為Throwable使用catch子句:

try {
    // Statement(s) that cause(s) the affected class to be loaded
} catch (Throwable t) {
    Logger.getLogger("<logger-name>").info("Loading my class went wrong", t);
}

當我將另一個模塊的 Maven 依賴項添加到我的項目時出現此錯誤,最終通過在我的程序的 JVM 選項中添加-Xss2m解決了這個問題(自 JDK5.0 以來默認為 1 兆字節)。 相信程序沒有足夠的堆棧來加載類。

嘗試在 Tomcat/JBOSS 服務器上部署應用程序時遇到NoClassDefFoundError 我使用不同的依賴項來解決問題,但不斷收到相同的錯誤。 將所有 javax.* 依賴項標記為 pom.xml 中提供的,並且 war 實際上沒有依賴項。 問題還是不斷出現。

終於意識到src/main/webapps/WEB-INF/ classesclasses文件夾,該文件夾被復制到我的 war 中,因此這些類不是編譯的類,而是被復制,因此沒有依賴項更改解決了這個問題。

因此,如果要復制任何以前編譯的數據,請小心,刪除類文件夾和重新編譯后,它起作用了!..

如果有人因為java.lang.NoClassDefFoundError: org/apache/log4j/Logger錯誤而來到這里,在我的情況下,它是因為我使用 log4j 2(但我沒有添加它附帶的所有文件)而產生的,還有一些使用 log4j 1 的依賴庫。解決方案是添加 Log4j 1.x 橋:log4j 2 附帶的 jar log4j-1.2-api-<version>.jar 。更多信息在 log4j 2遷移中。

此錯誤可能是由未經檢查的Java 版本要求引起的。

在我的例子中,我能夠在構建一個備受矚目的開源項目時解決這個錯誤,方法是使用SDKMAN 從 Java 9 切換到 Java 8! .

sdk list java
sdk install java 8u152-zulu
sdk use java 8u152-zulu

然后如下所述進行全新安裝。


當使用Maven作為你的構建工具時,有時會很有幫助——而且通常是令人滿意的,在test disabled的情況下進行干凈的“安裝”構建。

mvn clean install -DskipTests

現在一切都已構建和安裝,您可以繼續運行測試。

mvn test

當我沒有在項目的 Java 構建路徑的“訂購和導出”選項卡上導出類時,出現 NoClassDefFound 錯誤。 確保在添加到項目構建路徑的任何依賴項的“訂購和導出”選項卡中打勾。 請參閱Eclipse 警告:XXXXXXXXXXX.jar 將不會被導出或發布。 運行時 ClassNotFoundExceptions 可能會導致

也可能是因為您從具有特定包名稱的 IDE 復制代碼文件,並且您想嘗試使用終端運行它。 您必須先從代碼中刪除包名稱。 這發生在我身上。

就我而言,由於 JDK 版本不匹配,我收到此錯誤。 當我嘗試從 Intelij 運行該應用程序時,它無法正常工作,但隨后從命令行運行它就可以了。 這是因為 Intelij 試圖使用已設置的 Java 11 JDK 運行它,但在命令行上它使用 Java 8 JDK 運行。 在“文件”>“項目結構”>“項目設置”>“項目 SDK”下切換該設置后,它對我有用。

每個人都在這里談論一些 Java 配置內容、JVM 問題等,在我的情況下,錯誤與這些主題完全無關,並且有一個非常簡單且易於解決的原因:我的 Controller 的端點有一個錯誤的注釋( Spring Boot 應用程序)。

我在使用 Liberty 服務器的 JavaEE 中遇到了一個有趣的問題,即 NoClassDefFoundError。 我正在使用 IMS 資源適配器,而我的 server.xml 已經有 imsudbJXA.rar 的資源適配器。 當我為 imsudbXA.rar 添加新適配器時,我會開始為 DLIException、IMSConnectionSpec 或 SQLInteractionSpec 的實例對象收到此錯誤。 我不知道為什么,但我通過只使用 imsudbXA.rar 為我的工作創建新的 server.xml 來解決它。 我確信在 server.xml 中使用多個資源適配器很好,我只是沒有時間研究。

更新 [https://www.infoq.com/articles/single-file-execution-java11/]:

在 Java SE 11 中,您可以選擇直接啟動單個源代碼文件,而無需中間編譯。 只是為了您的方便,讓像您這樣的新手不必運行 javac + java (當然,讓他們感到困惑為什么會這樣)。

我有這個錯誤,但無法根據這個線程找出解決方案,但我自己解決了。

對於我的問題,我正在編譯這段代碼:

package valentines;

import java.math.BigInteger;
import java.util.ArrayList;

public class StudentSolver {
    public static ArrayList<Boolean> solve(ArrayList<ArrayList<BigInteger>> problems) {
        //DOING WORK HERE
        
    }
    public static void main(String[] args){
        //TESTING SOLVE FUNCTION
    }
    
}

然后我在一個文件夾結構中編譯這段代碼,就像 /ProjectName/valentines 編譯它工作正常但試圖執行: java StudentSolver

我得到了 NoClassDefError。

為了解決這個問題,我簡單地刪除了: package valentines;

我不太精通java包等,但這就是我修復錯誤的方式,如果其他人已經回答了這個問題,我很抱歉,但我無法將其解釋為我的問題。

我對此的解決方案是“利用”缺少的特定類的類路徑內容。 在我的例子中,我有 2 個依賴項,雖然我能夠使用 javac... 成功編譯,但我無法使用 java... 運行生成的類文件,因為 BouncyCastle jar 中的動態類不能在運行時加載。

javac --classpath "ext/commons-io-2.11.0;ext/bc-fips-1.0.2.3" hello.java

所以在編譯時和運行時,JVM 知道從哪里獲取 Apache Commons 和 BouncyCastle 依賴項,但是,在運行它時,我得到了

Error: Unable to initialize main class hello
Caused by: java.lang.NoClassDefFoundError: 
org/bouncycastle/jcajce/provider/BouncyCastleFipsProvider

因此,我根據類路徑在同一位置手動創建了一個名為 ext 的新文件夾,然后我將 BouncyCastle jar 放置在該文件夾中以確保在運行時可以找到它。 您可以相對於類文件或 jar 文件放置 jar,只要生成的清單具有指定的 jar 位置即可。 注意我只需要利用一個包含丟失的類文件的罐子。

我也遇到了同樣的問題,而且我存貨了好幾個小時。

我找到了解決方案。 就我而言,因此定義了靜態方法。 JVM 不能創建該類的另一個對象。

例如,

private static HttpHost proxy = new HttpHost(proxyHost, Integer.valueOf(proxyPort), "http");

Java 在運行時找不到類 A。 A 類位於來自不同工作區的 Maven 項目 ArtClient 中。 所以我將 ArtClient 導入到我的 Eclipse 項目中。 我的兩個項目使用 ArtClient 作為依賴項。 我將庫引用更改為這些項目的引用(構建路徑-> 配置構建路徑)。

問題消失了。

從 SRC 庫中刪除兩個文件后,我收到了此消息,當我將它們帶回來時,我一直看到此錯誤消息。

我的解決方案是:重新啟動 Eclipse。 從那以后我再也沒有看到這條消息:-)

確保這在module:appmodule:lib中匹配:

android {
    compileSdkVersion 23
    buildToolsVersion '22.0.1'
    packagingOptions {
    }

    defaultConfig {
        minSdkVersion 17
        targetSdkVersion 23
        versionCode 11
        versionName "2.1"
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM