【问题标题】:Probable bug in java compiler for openjdk8, how to report and get it fixed?openjdk8 的 java 编译器中可能存在错误,如何报告并修复它?
【发布时间】:2020-02-13 12:29:33
【问题描述】:

我已经准备了演示问题的 docker 图像:

https://drive.google.com/uc?id=1i04_dVL0Rp5rxXCMuHaS4LYREkZjAAW1&export=download

这张图基本是alpine:3.11+apk add openjdk8 maven+my maven project containing sample minimal java class that shows problem

您可以使用以下命令尝试:

# docker load -i bugexample.img
# docker run -w /root/bug-example --name bugtest bugexample /bin/ash build-until-sha-different.sh

如果你足够幸运(有时需要多次尝试),你会得到以下输出:

Found! Sha1 of two subsequent otherwise identical builds are different!
--- 1.sha1
+++ 2.sha1
@@ -1,3 +1,3 @@
 d8d46555c93da579adefc629f1764965a5493edb  com/SimpleBug$1.class
 75007242aab1e1877d24124d432cb246a79476a8  com/SimpleBug$SimpleBugBuilder.class
-23e8d0ea909b95a7955e0ec0adb4d12ae2193dd1  com/SimpleBug.class
+6a303d69d3f382b23ca04caee4102ee1cd7151e3  com/SimpleBug.class

这个构建的核心问题是它几乎每次构建时都会产生不同的字节码,即使没有其他任何东西(环境和代码本身)都没有改变。

当我比较这些不同的类文件时,我发现它们在一个字节上有所不同:

# cmp -lb 1_SimpleBug.class 2_SimpleBug.class
4053  66 6     65 5

深入研究类文件结构,我发现这种差异来自于 stackmapframe 常量池指针(StackMapTable 属性 -> 带有标签 Object_variable_info -> cpool_index 的 stack_map_frame 条目)

1_SimpleBug.class
   #35 = Utf8               supSetStringParameter10
   #36 = Utf8               Lcom/google/common/base/Supplier;

2_SimpleBug.class
   #35 = Utf8               supSetStringParameter10
   #36 = Utf8               Lcom/google/common/base/Supplier;

所以一个文件指向#35,另一个指向#36。我不认为这是正确的行为。

我想将此提交给适当的问题跟踪器,但我不知道该怎么做,因为所有相关的 JDK 跟踪器都仅供开发人员使用。

# java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (IcedTea 3.15.0) (Alpine 8.242.08-r0)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)

# mvn -version
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /usr/share/java/maven-3
Java version: 1.8.0_242, vendor: IcedTea, runtime: /usr/lib/jvm/java-1.8-openjdk/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-1050-kvm", arch: "amd64", family: "unix"

这里是java项目的存档:

https://drive.google.com/uc?id=1ZBdRzUk00QtpkGGnKAzipMjMtcnkKGN4&export=download

【问题讨论】:

  • 规范中没有规定两个后续构建必须产生相同的字节码。
  • 这不是提出好问题的方式。在问题中放置一个最小的可重现示例。 (想一想:为什么我们会相信你在 docker 镜像中放入的内容?)
  • 如果你真的想提交错误报告,请使用bugreport.java.com/bugreport,但你会浪费你的时间。这不是错误。这就是 Sun / Oracle / OpenJDK java 编译器一直以来的工作方式。
  • this answer 解决了即使相同版本在某些情况下也可能产生不同输出的事实,并且考虑到 reproducible-builds.org 的前提,还提到即使相同的类文件也不会生成相同的 jar 文件,因为它们确实包含时间戳。而在 Java 中,构建通常以 jar 文件或带有嵌入式 jar 文件的二进制文件结束……

标签: docker java-8


【解决方案1】:

嗯……劳动之山……

这一切都是徒劳的。 Maven是罪魁祸首。第一次运行我的构建时,它会下载许多 maven 需要的东西(除了我的项目依赖项),然后在同一个 jvm 中开始编译。第二次构建运行无需下载,因此编译立即开始。我不知道确切原因,但编译器的第一次调用会受到 jvm 状态的影响,这会导致它产生略有不同的字节码。

解决方案是将<fork>true<fork> 添加到我的 pom.xml 文件中,现在构建是完全可重现的......虽然需要大约 2 倍的时间 :)

我仍然认为即使初始编译发生在 maven jvm 中也不应该发生这种情况,它可能仍然是 javac 改进的主题,但这种“解决方法”是可以接受的。

【讨论】:

    猜你喜欢
    • 2019-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多