如果没有未使用的类,JVM会抛出吗?

mer*_*ike 9 java jvm linker-errors

考虑该计划:

public class Test {

    public static void main(String[] args) {
        if (Arrays.asList(args).contains("--withFoo")) {
            use(new Foo());
        }
    }

    static void use(Foo foo) {
        // do something with foo
    }
}
Run Code Online (Sandbox Code Playgroud)

如果在没有参数的情况下启动程序,那么运行时类路径中是否需要Foo?

研究

报告链接错误时,Java语言规范相当模糊:

该规范允许实现灵活性,以便何时发生链接活动(以及由于递归,加载),只要遵循Java编程语言的语义,在初始化之前完全验证和准备类或接口,并且在链接期间检测到的错误被抛出到程序中的某个点,在该点上程序可能需要链接到错误中涉及的类或接口.

我的测试表明只有在我实际使用时才会抛出LinkageErrors Foo:

$ rm Foo.class

$ java Test

$ java Test --withFoo

Exception in thread "main" java.lang.NoClassDefFoundError: Foo
        at Test.main(Test.java:11)
Caused by: java.lang.ClassNotFoundException: Foo
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.net.URLClassLoader$1.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 1 more
Run Code Online (Sandbox Code Playgroud)

可以依赖这种行为吗?或者是否有任何主流JVM链接未使用的代码?如果是这样,我如何隔离未使用的代码,以便仅在需要时才链接?

Hol*_*ger 8

您只需对测试代码进行少量更改即可回答该问题.

将类型层次更改为

class Bar {}
class Foo extends Bar {}
Run Code Online (Sandbox Code Playgroud)

和程序

public class Test {
    public static void main(String[] args) {
        if (Arrays.asList(args).contains("--withFoo")) {
            use(new Foo());
        }
    }
    static void use(Bar foo) {
        // don't need actual code
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,Foo即使在输入main方法之前(使用HotSpot),程序也将失败并显示错误(如果不存在).原因是验证者需要定义Foo以检查是否将其传递给期望的方法Bar是有效的.

如果类型是完全匹配或者目标类型是java.lang.Object,则HotSpot采用快捷方式,而不是加载类型,其中赋值始终有效.这就是为什么你的原始代码在Foo缺席时不会提前抛出的原因.

底线是抛出错误的确切时间点取决于实现,例如可能取决于实际的验证器实现.正如您已经引用的那样,所有保证的是,尝试执行需要链接的操作将抛出先前检测到的链接错误.但是你的程序完全有可能永远不会到目前为止尝试.

  • 如果您想真正独立于 JVM 实现,则应遵循 Chris Parker 的回答。对于 Oracle 的 JVM(HotSpot 或 OpenJDK),验证是在每个方法的基础上执行的,因此从未调用过的方法中的引用无效。但这就是这个特殊的 JVM。 (2认同)