输出-1在循环中变为斜线 [英] The output -1 becomes a slash in the loop

查看:63
本文介绍了输出-1在循环中变为斜线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

令人惊讶的是,以下代码输出:

Surprisingly, the following code outputs:

/
-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,而没有任何问题.谁能告诉我为什么?

I tried many times to determine how many times this would occur, but, unfortunately, it was ultimately uncertain, and I found that the output of -2 sometimes turned into a period. In addition, I also tried to remove the while loop and output -1 without any problems. Who can tell me why?

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).

This can be reliably reproduced (or not reproduced, depending on what you want) with openjdk version "1.8.0_222" (used in my analysis), OpenJDK 12.0.1 (according to Oleksandr Pyrohov) and OpenJDK 13 (according to Carlos Heuberger).

我用-XX:+PrintCompilation运行了足够的时间来获得两种行为,这就是区别.

I ran the code with -XX:+PrintCompilation enough times to get both behaviours and here are the differences.

Buggy实现(显示输出):

Buggy implementation (displays output):

 --- 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 次.

We can notice one significant difference. With the correct execution we compile test() twice. Once in the beginning, and once again afterwards (presumably because the JIT notices how hot the method is). In the buggy execution test() is compiled (or decompiled) 5 times.

另外,使用-XX:-TieredCompilation(可解释或使用C2)-Xbatch(强制编译在主线程中运行,而不是并行运行)一起运行,输出得到保证,并且经过30000次迭代可以打印出很多内容,因此C2编译器似乎是罪魁祸首.通过运行-XX:TieredStopAtLevel=1可以确认这一点,该功能禁用C2并且不产生输出(在4级停止再次显示该错误).

Additionally, running with -XX:-TieredCompilation (which either interprets, or uses C2) or with -Xbatch (which forces the compilation to run in the main thread, instead of parallelly), the output is guaranteed and with 30000 iterations prints out a lot of stuff, so the C2 compiler seems to be the culprit. This is confirmed by running with -XX:TieredStopAtLevel=1, which disables C2 and doesn't produce output (stopping at level 4 shows the bug again).

在正确的执行中,首先使用第3级编译,然后是第4级.

In the correct execution, the method is first compiled with Level 3 compilation, then afterwards with Level 4.

在越野车执行过程中,先前的编译将被舍弃(made non entrant),然后再次在3级(即C1,请参见上一链接)上进行编译.

In the buggy execution, the previous compilations are discared (made non entrant) and it's again compiled on Level 3 (which is C1, see previous link).

因此,这绝对是C2中的错误,尽管我不确定要返回到3级编译的事实是否会对其造成影响(以及为什么返回到3级,仍然存在许多不确定性)

So it definitely is a bug in C2, although I'm not absolutely sure whether the fact that it's going back to Level 3 compilation affects it (and why is it going back to level 3, so many uncertainties still).

您可以使用以下代码行生成汇编代码,以更深入地钻入兔子孔(另请参见

You can generate the assembly code with the following line to go even deeper into the rabbit hole (also see this to enable assembly printing).

java -XX:+PrintCompilation -Xbatch -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LoopOutPut > broken.asm

在这一点上,我开始精疲力尽,当放弃以前的编译版本时,越野车的行为开始显现出来,但是我具备90年代的装配技巧,所以我会让一个比...更聪明的人我从这里拿走.

At this point I'm starting to run out of skills, the buggy behaviour starts to exhibit when the previous compiled versions are discarded, but what little assembly skills I have are from the 90's, so I'll let someone smarter than me take it from here.

由于该代码是由其他人提供给OP的,并且所有代码

It's likely that there is already a bug report about this, since the code was presented to the OP by someone else, and as all code C2 isn't without bugs. I hope this analysis has been as informative to others as it has been to me.

正如评论中古老的apangin所指出的,这是一个最近的错误.所有有兴趣和乐于助人的人都有很多义务:)

As the venerable apangin pointed out in the comments, this is a recent bug. Much obliged to all the interested and helpful people :)

这篇关于输出-1在循环中变为斜线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆