Java 类文件的创建是否具有确定性?

2022-08-31 10:52:47

使用相同的JDK(即相同的可执行文件)时,生成的类文件是否始终相同?根据操作系统硬件的不同,是否会有所不同?除了JDK版本,是否还有其他因素导致差异?是否有任何编译器选项可以避免差异?差异仅在理论上可能,还是Oracle实际上为相同的输入和编译器选项生成不同的类文件?javacjavac

更新 1我感兴趣的是生成,即编译器输出,而不是类文件是否可以在各种平台上运行

更新 2通过“相同的JDK”,我也意味着相同的可执行文件。javac

更新 3Oracle编译器中理论差异和实际差异之间的区别。

[编辑,添加转述问题]
“在什么情况下,相同的javac可执行文件在不同的平台上运行时会产生不同的字节码?”


答案 1

让我们这样说:

我可以很容易地生成一个完全符合的Java编译器,在给定同一个文件的情况下,它永远不会生成相同的文件两次。.class.java

我可以通过调整各种字节码构造或简单地向我的方法添加多余的属性(这是允许的)来做到这一点。

鉴于该规范要求编译器生成字节对字节的相同类文件,我将避免依赖这样的结果。

但是,我检查了几次,使用相同的编译器使用相同的开关(和相同的库)编译相同的源文件!确实导致相同的文件。.class

更新:我最近偶然发现了这篇有趣的博客文章,内容是关于在Java 7中String上实现switch的。在这篇博客文章中,有一些相关的部分,我将在这里引用(强调我的):

为了使编译器的输出可预测和可重复,这些数据结构中使用的映射和集合是 s 和 s,而不仅仅是 和 。就给定编译期间生成的代码的功能正确性而言使用HashMapHashSet就可以了;迭代顺序无关紧要。但是,我们发现让javac的输出不会根据系统类的实现细节而变化是有益的LinkedHashMapLinkedHashSetHashMapsHashSets

这非常清楚地说明了这个问题:编译器不需要以确定性的方式行事,只要它与规范匹配即可。但是,编译器开发人员意识到,尝试下通常是一个好主意(前提是它可能不太昂贵)。


答案 2

编译器没有义务在每个平台上生成相同的字节码。您应该咨询不同供应商的实用程序以获得特定的答案。javac


我将通过文件排序展示一个实际示例。

假设我们有 2 个 jar 文件:和 .它们并排放在目录中。编译器按字母顺序读取它们(因为这是 ),但是当文件系统不区分大小写时,顺序是 ,如果它是区分大小写的。my1.jarMy2.jarliblibmy1.jarMy2.jarMy2.jarmy1.jar

具有具有方法的类my1.jarA.class

public class A {
     public static void a(String s) {}
}

具有相同的,但具有不同的方法签名(接受):My2.jarA.classObject

public class A {
     public static void a(Object o) {}
}

很明显,如果你有一个电话

String s = "x"; 
A.a(s); 

它将在不同情况下编译具有不同签名的方法调用。因此,根据您的文件系统大小写敏感性,您将获得不同的类。


推荐