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



给定以下使用Java 8Optional:的类

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

当我使用Java7JVM运行时,main会抛出一个NoClassDefFoundError,并按预期为java.util.Optional包装一个ClassNotFoundException

但是,如果我在使用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;
}
}
}

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

Optional not found.

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

我仔细看了一遍";第五章。加载、链接和初始化";JVM规范和";15.12.4.方法调用的运行时评估";在JLS中。

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

不能保证类不会被加载。

考虑JLS,§5.4:

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

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

即使是使用延迟类加载的HotSpot JVM,也可能会尝试比预期更早地加载类,即在未使用的代码路径之外,因为代码的细微方面可能需要验证器加载类,如"何时加载Java类?

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

相关内容