【发布时间】: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 文件的二进制文件结束……