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

mst*_*rap 93 java compiler-construction javac

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

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

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

更新3 Oracle编译器中理论差异与实际差异的区别.

[编辑,添加释义问题]
"在不同的平台上运行相同的javac可执行文件会产生不同的字节码的情况是什么?"

Joa*_*uer 68

我们这样说吧:

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

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

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

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

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

为了使编译器的输出可预测和可重复,这些数据结构中使用的映射和集合是LinkedHashMaps和LinkedHashSets而不仅仅是HashMapsHashSets.给定编译期间生成的代码的功能正确性方面,使用HashMap并且HashSet会很好 ; 迭代顺序无关紧要.但是,我们发现javac根据系统类的实现细节,输出不会有所不同.

这很清楚地说明了问题:编译器不需要以确定的方式操作,只要它与规范匹配即可.然而,编译器开发人员意识到尝试通常是一个好主意(假设它不太昂贵,可能).

  • 好吧,对我而言,这是足够的理由不依赖它:如果我依赖于编译器总是生成相同代码的事实,更新的JDK可能会破坏我的构建/归档系统. (3认同)
  • @GaborSch:你已经有了这种情况的一个很好的例子,所以对问题的一些额外的看法是有序的.复制你的作品是没有意义的. (3认同)
  • @mstrap:毕竟这是一个XY问题.好吧,你可以查看jar的差异更新(所以即使是一个字节的差异也不会导致整个jar被重载),你应该为你的版本提供明确的版本号,所以在我看来,这一点都没有实际意义. . (2认同)

gab*_*sch 38

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


我将通过文件排序显示一个实际的例子.

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

my1.jar具有类A.class以的方法

public class A {
     public static void a(String s) {}
}
Run Code Online (Sandbox Code Playgroud)

My2.jar具有相同的A.class,但具有不同的方法签名(接受Object):

public class A {
     public static void a(Object o) {}
}
Run Code Online (Sandbox Code Playgroud)

很明显,如果你有电话

String s = "x"; 
A.a(s); 
Run Code Online (Sandbox Code Playgroud)

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

  • @GaborSch我对同一个JDK的字节码是否相同感兴趣,即相同的javac.我会更清楚. (2认同)
  • @mstrap我理解你的问题,但答案仍然是相同的:取决于供应商.`javac`不一样,因为你在每个平台上都有不同的二进制文件(例如Win7,Linux,Solaris,Mac).对于供应商而言,拥有不同的实现是没有意义的,但任何特定于平台的问题都会影响结果(例如目录中的flie排序(想想你的`lib`目录),endianness等). (2认同)
  • @mstrap - 他所提出的观点是,任何供应商都没有*要求*使他们的编译器在平台上产生完全相同的字节码,只是结果字节码产生相同的结果.鉴于没有标准/规范/要求,您的问题的答案是"这取决于具体的供应商,编译器和平台". (2认同)
  • @mstrap你问的是理论问题,所以理论上答案是**否**。但*实际上*大多数供应商都会生成相同的代码,但不能保证这一点。您对 JDK8 的第一个版本将在所有平台上生成相同的字节码有多大信心? (2认同)

mtk*_*mtk 6

简答 - 没有


答案很长

bytecode对于不同的平台,它们不一定相同.它是JRE(Java运行时环境),它知道如何执行字节码.

如果您仔细阅读Java VM规范,您将会发现,对于不同的平台,字节码是相同的,这不一定是真的.

通过类文件格式,它将类文件的结构显示为

ClassFile {
    u4 magic;
    u2 minor_version;
    u2 major_version;
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags;
    u2 this_class;
    u2 super_class;
    u2 interfaces_count;
    u2 interfaces[interfaces_count];
    u2 fields_count;
    field_info fields[fields_count];
    u2 methods_count;
    method_info methods[methods_count];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}
Run Code Online (Sandbox Code Playgroud)

检查次要版本和主要版本

minor_version,major_version

minor_version和major_version项的值是此类文件的次要版本号和主要版本号.总的来说,主要版本号和次要版本号决定了类文件格式的版本.如果类文件具有主版本号M和次版本号m,则我们将其类文件格式的版本表示为Mm.因此,类文件格式版本可以按字典顺序排序,例如,1.5 <2.0 <2.1.当且仅当v位于某个连续范围Mi.0 v Mj.m中时,Java虚拟机实现可以支持版本v的类文件格式.只有Sun可以指定符合Java平台特定发行版级别的Java虚拟机实现可支持的版本范围

通过脚注阅读更多内容

1 Sun的JDK 1.0.2版的Java虚拟机实现支持45.0到45.3的类文件格式版本.Sun的JDK 1.1.X版可以支持45.0到45.65535范围内的类文件格式.Java 2平台1.2版的实现可以支持45.0到46.0范围内的版本的类文件格式.

因此,调查所有这些表明在不同平台上生成的类文件不必相同.