字节上的java按位运算

时间:2015-01-28 13:57:25

标签: java

public static void main(String[] args) throws Exception {
    for (int i = 0, k = 20; i < Integer.MAX_VALUE; i++) {
        final Byte b = (byte) -1; // new Byte((byte) -1) works fine
        final int x = b.byteValue() & 0xff;
        if (x == -1) System.out.println(x + " is -1 (" + i + ")");
        if (x != 255) {
            System.out.println(x + " is not 255 (" + i + ")");
            if (x == -1) System.out.println(x + " is not 255 but -1 (" + i + ")");
            if (--k == 0) break;
        }
        if (i % 100 == 0) Thread.sleep(1); // or other operations to make this code run slower
    }
}

运行输出:

-1 is not 255 (110675)
-1 is not 255 but -1 (110675)
/ is not 255 (168018)
/ is not 255 (168019)
/ is not 255 (168020)
/ is not 255 (168021)
/ is not 255 (168022)
/ is not 255 (168023)
/ is not 255 (168024)
/ is not 255 (168025)
/ is not 255 (168026)
/ is not 255 (168027)
/ is not 255 (168028)
/ is not 255 (168029)
/ is not 255 (168030)
/ is not 255 (168031)
/ is not 255 (168032)
/ is not 255 (168033)
/ is not 255 (168034)
/ is not 255 (168035)
/ is not 255 (168036)

谁能解释输出结果。测试必须在jdk1.8.0_20或jdk1.8.0_25或jdk1.8.0_31以及“-server”选项下运行。我认为这是一个错误,我已经向oracle提交了一个错误报告,但还没有收到回复。

1 个答案:

答案 0 :(得分:3)

我想我发现导致这种行为的问题;它是以bug 8042786的形式提交给OpenJDK的,确实与自动装箱有关。使用-XX:-EliminateAutoBox运行Java似乎是一种有效的解决方法,并使此问题消失。

标记为已解决。链接的错误报告是一个有趣的读物,该错误显然最初在a SO question中找到。

修改

从错误报告和带有此问题的VM的编译日志判断,这是我对更确切地说发生的事情的分析:

对于前110674次迭代,代码在解释器中运行,并且由没有此错误的C1编译器编译,但在此之后,C2编译器开始用main替换b.byteValue() & 0xff。更优化的版本。在第一轮编译中,C2执行以下操作:

  • 从表达式x,它得出x == -1在0到255之间的结论。因此,java.lang.System的测试不可能是真的,并且已经过优化。
  • 此时,类System.out.println仍然没有被加载,所以对b.byteValue() & 0xff的所有调用都被取消优化该方法的陷阱所取代,并恢复为等待类加载的解释器和重新编译。
  • 要评估and表达式,编译器足够聪明,可以内联无符号内存字节加载,从内存中获取值,而无需对其执行以下x操作,以便单独的单指令应该填充movsbl的值,但这是编译器错误的来源,并错误地将其替换为带符号的加载(x指令)。所以x实际上已经加载了32位的完整符号扩展,但编译器已经假设x == -1的值在0-255范围内。

然后发生的是x != 255测试没有触发,因为C2已经优化了它,但是x测试确实触发了,因为编译器没有足够聪明地看到它Byte.valueOf()应始终为255(我发现这有点奇怪,因为b.byteValue()System.out都已内联,但显然它就是这样的。当它进入以下子句时,代码会尝试加载x,它会立即取消优化代码,加载类,并在解释器中完成迭代。这就是java.lang.System&#34;正确&#34;在本次迭代的其余部分中被视为-1。

然后循环继续在解释器和C1编译的代码中再进行几千次迭代,直到C2再次启动,以便在加载x == -1的情况下重新优化代码。然后会发生以下情况:

  • 同样,java.lang.System的测试被优化为不可能。
  • 但是,此时加载x + " is not 255 (" + i + ")",并且内联格式Integer.toString()的调用。查看x的来源,这意味着消除了负整数输出的测试(同样,x被确定为正),因此'0' + -1的格式如同它是1位数的正数(因为它小于10)。因此,这一位数输出为/;也就是说,{{1}}。

这可能是最好的琐事,但它引起了我的注意。 :)