是否在 JVM/JLS 中指定永远不会加载未使用的代码路径中的 类?

Is it specified in the JVM/JLS that classes in unused code paths will never be loaded?

给定以下 class 使用 Java 8 Optional:

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

如果我使用 Java 8 编译器编译代码,目标是 Java 7 字节码:

javac -target 1.7 -source 1.7 Main.java

当我 运行 使用 Java 7 JVM 时,main 按预期抛出一个 NoClassDefFoundErrorjava.util.Optional 包装一个 ClassNotFoundException

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

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 中是一致的,但我似乎无法在规范中找到任何要求 class 在方法中本地使用的 es 必须延迟加载。

我在 JLS 中查看了 "Chapter 5. Loading, Linking, and Initializing" of the JVM Spec and "15.12.4. Run-Time Evaluation of Method Invocation"

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

不保证 class 不会加载。

考虑 JLS, §5.4:

This specification allows an implementation flexibility as to when linking activities (and, because of recursion, loading) take place, provided that all of the following properties are maintained:

For example, a Java Virtual Machine implementation may choose a "lazy" linkage strategy, where each symbolic reference in a class or interface (other than the symbolic references above) is resolved individually when it is used. Alternatively, an implementation may choose an "eager" linkage strategy, where all symbolic references are resolved at once when the class or interface is being verified.

即使是使用惰性 class 加载的 HotSpot JVM 也可能会尝试比预期更早地加载 class,即在未使用的代码路径之外,由于代码的微妙方面,这可能需要验证者加载一个 class,如

中所讨论

换句话说,即使使用这个 JVM 实现,如果 class 不存在,对代码的小改动可能会突然导致它失败。