这可以可靠地复制(或不复制,取决于你想要什么),OpenJDK(根据Oleksandr Pyrohov)和OpenJDK 13(根据Carlos Heuberger)。openjdk version "1.8.0_222"
12.0.1
我运行了足够多的次数来获得这两种行为,这就是差异。-XX:+PrintCompilation
错误实现(显示输出):
--- Previous lines are identical in both
54 17 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
54 23 3 LoopOutPut::test (57 bytes)
54 18 3 java.lang.String::<init> (82 bytes)
55 21 3 java.lang.AbstractStringBuilder::append (62 bytes)
55 26 4 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
55 20 3 java.lang.StringBuilder::<init> (7 bytes)
56 19 3 java.lang.StringBuilder::toString (17 bytes)
56 25 3 java.lang.Integer::getChars (131 bytes)
56 22 3 java.lang.StringBuilder::append (8 bytes)
56 27 4 java.lang.String::equals (81 bytes)
56 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) made not entrant
56 28 4 java.lang.AbstractStringBuilder::append (50 bytes)
56 29 4 java.lang.String::getChars (62 bytes)
56 24 3 java.lang.Integer::stringSize (21 bytes)
58 14 3 java.lang.String::getChars (62 bytes) made not entrant
58 33 4 LoopOutPut::test (57 bytes)
59 13 3 java.lang.AbstractStringBuilder::append (50 bytes) made not entrant
59 34 4 java.lang.Integer::getChars (131 bytes)
60 3 3 java.lang.String::equals (81 bytes) made not entrant
60 30 4 java.util.Arrays::copyOfRange (63 bytes)
61 25 3 java.lang.Integer::getChars (131 bytes) made not entrant
61 32 4 java.lang.String::<init> (82 bytes)
61 16 3 java.util.Arrays::copyOfRange (63 bytes) made not entrant
61 31 4 java.lang.AbstractStringBuilder::append (62 bytes)
61 23 3 LoopOutPut::test (57 bytes) made not entrant
61 33 4 LoopOutPut::test (57 bytes) made not entrant
62 35 3 LoopOutPut::test (57 bytes)
63 36 4 java.lang.StringBuilder::append (8 bytes)
63 18 3 java.lang.String::<init> (82 bytes) made not entrant
63 38 4 java.lang.StringBuilder::append (8 bytes)
64 21 3 java.lang.AbstractStringBuilder::append (62 bytes) made not entrant
正确运行(无显示):
--- Previous lines identical in both
55 23 3 LoopOutPut::test (57 bytes)
55 17 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
56 18 3 java.lang.String::<init> (82 bytes)
56 20 3 java.lang.StringBuilder::<init> (7 bytes)
56 21 3 java.lang.AbstractStringBuilder::append (62 bytes)
56 26 4 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
56 19 3 java.lang.StringBuilder::toString (17 bytes)
57 22 3 java.lang.StringBuilder::append (8 bytes)
57 24 3 java.lang.Integer::stringSize (21 bytes)
57 25 3 java.lang.Integer::getChars (131 bytes)
57 27 4 java.lang.String::equals (81 bytes)
57 28 4 java.lang.AbstractStringBuilder::append (50 bytes)
57 10 3 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes) made not entrant
57 29 4 java.util.Arrays::copyOfRange (63 bytes)
60 16 3 java.util.Arrays::copyOfRange (63 bytes) made not entrant
60 13 3 java.lang.AbstractStringBuilder::append (50 bytes) made not entrant
60 33 4 LoopOutPut::test (57 bytes)
60 34 4 java.lang.Integer::getChars (131 bytes)
61 3 3 java.lang.String::equals (81 bytes) made not entrant
61 32 4 java.lang.String::<init> (82 bytes)
62 25 3 java.lang.Integer::getChars (131 bytes) made not entrant
62 30 4 java.lang.AbstractStringBuilder::append (62 bytes)
63 18 3 java.lang.String::<init> (82 bytes) made not entrant
63 31 4 java.lang.String::getChars (62 bytes)
我们可以注意到一个显着的差异。通过正确的执行,我们编译两次。一次在开始时,再一次在之后(大概是因为JIT注意到该方法有多热)。在 buggy 执行中被编译(或反编译)5 次。test()
test()
此外,运行(解释或使用)或与(强制编译在主线程中运行,而不是并行运行),输出是有保证的,并且30000次迭代打印出很多东西,所以编译器似乎是罪魁祸首。这可以通过运行 with 来确认,该操作会禁用并且不会产生输出(在级别 4 停止会再次显示 bug)。-XX:-TieredCompilation
C2
-Xbatch
C2
-XX:TieredStopAtLevel=1
C2
在正确执行中,首先使用级别 3 编译该方法,然后使用级别 4 编译该方法。
在 buggy 执行中,以前的编译被分解 (),然后再次在 Level 3 上编译(即 ,请参阅上一个链接)。made non entrant
C1
因此,它绝对是 中的一个错误,尽管我不完全确定它返回到级别 3 编译的事实是否会影响它(以及为什么它会返回到级别 3,仍然存在许多不确定性)。C2
您可以使用以下行生成装配体代码,以更深入地进入兔子洞(另请参阅此图以启用装配体打印)。
java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm
在这一点上,我开始耗尽技能,当以前的编译版本被丢弃时,错误的行为开始表现出来,但是我所拥有的一点点组装技能来自90年代,所以我会让比我更聪明的人从这里拿走它。
很可能已经有一个关于此的错误报告,因为代码是由其他人提供给OP的,并且因为所有代码C2都不是没有错误的。我希望这一分析对其他人和我一样具有信息量。
正如尊敬的apangin在评论中指出的那样,这是最近的一个错误。非常有义务:)所有感兴趣和乐于助人的人