令人惊讶的是,以下代码输出:
/
-1
代码:
public class LoopOutPut {
public static void main(String[] args) {
LoopOutPut loopOutPut = new LoopOutPut();
for (int i = 0; i < 30000; i++) {
loopOutPut.test();
}
}
public void test() {
int i = 8;
while ((i -= 3) > 0) ;
String value = i + "";
if (!value.equals("-1")) {
System.out.println(value);
System.out.println(i);
}
}
}
我尝试了很多次以确定这种情况会发生多少次,但是不幸的是,最终还是不确定的,并且我发现-2的输出有时变成了一个周期。此外,我还尝试了删除while循环并输出-1,而没有任何问题。谁能告诉我为什么?
JDK版本信息:
HopSpot 64-Bit 1.8.0.171
IDEA 2019.1.1
可以(根据openjdk version "1.8.0_222"
我的分析使用),OpenJDK 12.0.1
(根据Oleksandr
Pyrohov)和OpenJDK 13(根据Carlos Heuberger)可靠地复制(或复制,取决于您想要的内容)。
我运行了-XX:+PrintCompilation
足够的时间来获得两种行为的代码,这就是区别。
Buggy实现(显示输出):
--- 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)
我们可以注意到一个明显的不同。正确执行后,我们将编译test()
两次。刚开始时,然后又一次(可能是因为JIT注意到该方法有多热)。在越野车中,执行test()
被编译(或反编译)
5 次。
此外,与运行-XX:-TieredCompilation
(这两种解释,或使用C2
) 或
用-Xbatch
(这迫使编译在主线程中运行,而不是平行),输出 保证
,并与30000反复打印出了很多东西,所以C2
编译器似乎成为罪魁祸首。通过运行可以确认这一点-XX:TieredStopAtLevel=1
,它可以禁用C2
也不产生输出(在4级停止再次显示该错误)。
在正确执行中,首先使用3级编译来编译该方法,然后再使用4
级编译。
在越野车执行过程中,先前的编译将被舍弃(made non entrant
),然后再次在Level 3上进行编译(即C1
,请参见前面的链接)。
因此,这绝对是一个错误C2
,尽管我不确定它回到3级编译的事实是否会影响它(以及为什么回到3级,仍然有很多不确定性)。
您可以使用以下代码行生成汇编代码,以更深入地进入兔子孔(另请参阅此内容以启用汇编打印)。
java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm
在这一点上,我开始精疲力尽,当放弃以前的编译版本时,越野车的行为开始显现出来,但是我具备90年代的少量组装技能,所以我会让比我聪明的人来使用它从这里。
很可能已经有关于此的错误报告,因为代码是由其他人提供给OP的,并且所有代码C2都没有错误。我希望这种分析对我一样有益。
正如古老的apangin在评论中指出的,这是一个最近的bug。所有有兴趣和乐于助人的人都有义务:)
volist标签 标签名 作用 包含属性 volist 数组数据循环输出 name,id,offset,length,key,mod 标签属性: | 标签属性名 | 含义 | | --- | --- | | name |模板赋值的变量名称,必须 | | id |当前的循环变量名称,必须 | | offset |循环开始索引,默认从0开始 | | length |循环长度,默认为 null | |
volist标签 标签名 作用 包含属性 volist 数组数据循环输出 name,id,offset,length,key,mod 标签属性: | 标签属性名 | 含义 | | --- | --- | | name |模板赋值的变量名称,必须 | | id |当前的循环变量名称,必须 | | offset |循环开始索引,默认从0开始 | | length |循环长度,默认为 null | |
问题内容: 这是代码- 这是输出- 0 0 1 2 3 4 5 6 7 8 当我想在for循环退出后使用变量i时,出现了我的问题。我会假设我正在读取7,即从零开始的计数中的第8个增量,但实际上是8!在循环退出时对变量i又增加了一个增量。 为了解决这个问题,我必须在循环的末尾以及在其他任何代码中使用它之前,先执行类似i的操作。在我看来,这使代码更难理解。 有更好的解决方案吗? 问题答案: 当值为7时
<代码>car\U gear字段在数据库中填写为“stick”(斗杆)。在图像标记后,输出更改为“自动” 为什么结果是$car\u result1-
对于我的程序,我要编写一个程序,它接受2到10之间的行数。生成n行的乘法三角形。每行包含的条目不超过其行大小。这一点我没有问题。但是,在用户将数字0输入到我的问题“请输入要打印的行数:”之后,应该终止循环并打印“感谢您使用此程序!”我用了一个DO。。。WHILE循环以确定用户是否希望继续。在我的循环中,我将用户想要打印的数字声明为int num。我的循环应该持续到num
我一直在寻找如何弄清楚如何让一个输入或某些东西进入一个time循环。就像在,输入()命令不会停止我的秒表。我尝试过tkinter、pygame和其他几种方法,但它们都不起作用。如果有人能帮我,我更喜欢小而简单的东西,如果这是可能的话。具体来说,我想学习做什么,基本上是允许在按下任何键时立即停止(最好不按回车键)。谢谢,马鞍猪! 以下是我到目前为止的情况,没有任何东西可以激活停止: