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提交了一个错误报告,但还没有收到回复。
答案 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}}。这可能是最好的琐事,但它引起了我的注意。 :)