当可选库不在类路径上,但相关代码未执行时,出现意外的“NoClassDefFoundError”

问题描述 投票:0回答:1

我正在尝试使用 Java 代码中的 Google Closure Compiler,但希望它成为可选依赖项(在构建时存在,但在部署时可能不存在)。 我遇到的问题是,当库在运行时不在类路径上时,我会得到

NoClassDefFoundError
。 如果我要执行引用库符号的代码,这当然是预期的。但所有这些代码永远不会被执行(在标志后面)。事实上,当第一次访问包含该代码的类时,就会发生异常。

我现在对此一直很头疼,我想我可能错过了一些关于 JVM 中类加载语义的见解。

我已将问题简化为以下内容:

import com.google.javascript.jscomp.CompilerOptions;
import com.google.javascript.jscomp.StrictWarningsGuard;

public class Main {
    public static void main(String[] args) {
        System.out.println("HelloWorld");
    }

    private static void thisMethodIsNotCalled() {
        new CompilerOptions().addWarningsGuard(new StrictWarningsGuard());
    }
}

访问库的唯一代码位于从未被调用的方法中。

编译(从maven下载的jar):

javac -cp closure-compiler-v20240317.jar Main.java

跑步时,我得到一个

NoClassDefFoundError

$ java Main
Error: Unable to initialize main class Main
Caused by: java.lang.NoClassDefFoundError: com/google/javascript/jscomp/WarningsGuard

我本来希望程序能够成功运行。 JVM 没有理由尝试加载任何库类。

为什么会出现这种情况?我一直在查看 JVM Spec,但没有看到

Main
的加载将如何导致 JVM 尝试加载
thisMethodIsNotCalled
中引用的符号。

更奇怪的是,将

thisMethodIsNotCalled
的内容替换为以下内容,不会导致此错误,因此这似乎是由非常特定的交互引起的。

new StrictWarningsGuard();
new CompilerOptions().addWarningsGuard(null);

其他背景

Ubuntu 24.04 上的 OpenJDK 21 会发生这种情况:

$ java -version
openjdk version "21.0.3" 2024-04-16
OpenJDK Runtime Environment (build 21.0.3+9-Ubuntu-1ubuntu1)
OpenJDK 64-Bit Server VM (build 21.0.3+9-Ubuntu-1ubuntu1, mixed mode, sharing)

但我相信这并不特定于该版本,我也用各种其他版本复制了此内容。

java jvm classloader bytecode jvm-hotspot
1个回答
0
投票

我在 macOS 上使用 JDK 21.0.3 获取“HelloWorld”。您的 java 命令缺少类路径,但这会引发导入和符号错误。您的环境中的某些内容可能正在定义相同类的不同版本。这可能会导致

NoClassDefFoundError
异常。查看 JDK 中是否已有库,或者某些环境变量正在添加到类路径中。这可以解释为什么你的
java
命令也没有缺少类路径。

© www.soinside.com 2019 - 2024. All rights reserved.