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

查看:27
本文介绍了输出 -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 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).

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

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

在有问题的执行中,先前的编译被丢弃(非进入),并再次在第 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).

您可以使用以下行生成汇编代码以更深入地了解兔子洞(另请参阅 this 以启用装配打印).

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 的,并且所有代码都C2 并非没有错误.我希望这种分析对其他人和我一样有用.

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天全站免登陆