首页 > 解决方案 > 是否在 JVM/JLS 中指定永远不会加载未使用的代码路径中的类?

问题描述

给定以下使用 Java 8 的类Optional

final class Main {
    public static void main(final String[] args) {
        System.out.println(Optional.of("test").get());
    }
}

如果我使用针对 Java 7 字节码的 Java 8 编译器编译代码:

javac -target 1.7 -source 1.7 Main.java

当我使用 Java 7 JVM 运行时,会按预期main抛出一个NoClassDefFoundError包装 a ClassNotFoundExceptionfor 。java.util.Optional

Optional但是,如果我在使用它之前检查类的可用性(通过反射):

final class Main {
    public static void main(final String[] args) {
        if (isOptionalAvailable()) {
            System.out.println(Optional.of("test").get());
        } else {
            System.out.println("Optional not found.");
        }
    }

    private static boolean isOptionalAvailable() {
        try {
            Class.forName("java.util.Optional");
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}

当我使用 Java 7 JVM 运行时,它不会抛出任何错误:

Optional not found.

我试图找出的是 JVM 或 JLS 规范是否需要这种行为。似乎 Oracle、IBM 和 OpenJDK 的行为是一致的,但我似乎在规范中找不到任何要求在方法中本地使用的类必须延迟加载。

我浏览了 JVM Spec 的“第 5 章。加载、链接和初始化”和 JLS 中的“15.12.4. 方法调用的运行时评估”

Optional对于我的第二个示例,即使它只存在于未使用的代码路径中,是否存在急切加载的 JVM impl ?我是否错过了需要此行为的规范部分,或者它只是一个常见的实现细节?

标签: javajvmjls

解决方案


无法保证该类不会被加载。

考虑JLS,§5.4

该规范允许在何时发生链接活动(以及由于递归,加载)时实现灵活性,前提是维护以下所有属性:

…</p>

例如,Java 虚拟机实现可能会选择“惰性”链接策略,其中类或接口中的每个符号引用(除了上面的符号引用)在使用时都会单独解析。或者,实现可以选择“急切”的链接策略,其中在验证类或接口时立即解析所有符号引用。

即使是使用延迟类加载的 HotSpot JVM,也可能会尝试比预期更早地加载类,即在未使用的代码路径之外,由于代码的微妙方面,这可能需要验证器加载类,如何时所述是否加载了 Java 类?

换句话说,即使使用这个 JVM 实现,当类不存在时,对代码的微小更改也可能会突然导致它失败。


推荐阅读