4
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
    }
}

Output of a run:

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

who can explain the output result. the test must be running at jdk1.8.0_20 or jdk1.8.0_25 or jdk1.8.0_31 and with "-server" option. I think this is a bug, and I have submitted a bug report to oracle, but haven't received a reply yet.

14
  • 2
    What is the output? You haven't described it. Commented Jan 28, 2015 at 14:00
  • 2
    What is your expectation, and what is the output? Seeing that code, I would expect, that nothing should be printed. Commented Jan 28, 2015 at 14:02
  • 1
    That should not print anything and run more than 200 days. If it prints something, breaks on the k == 0 or so, there is a bug. Filing it with Oracle is the right way to go, but don't expect instant reactions. Commented Jan 28, 2015 at 14:08
  • 1
    The output is utterly crazy: the same check x == -1 first fails, then passes, and / is printed as an integer value. Not to mention the obvious failure. This can only be the deeds of the JIT compiler. Commented Jan 28, 2015 at 14:18
  • 1
    Also using the String constructor makes the problem go away - e.g. final Byte b = new Byte("-1"); Commented Jan 29, 2015 at 1:21

1 Answer 1

3

I think I found the issue that causes this behavior; it was filed with OpenJDK as bug 8042786 and is, indeed, related to autoboxing. Running Java with -XX:-EliminateAutoBox seems to be a valid workaround and makes this problem disappear.

It is marked as resolved. The linked bug reports are an interesting read, and the bug was apparently originally found in a SO question.

EDIT:

Judging from the bug reports and a compilation log from a VM with this problem, this is my analysis of what, more precisely, happened:

For the first 110674 iterations, the code runs in the interpreter and as compiled by the C1 compiler that doesn't have this bug, but after that, the C2 compiler kicks in to replace main with an even more optimized version. In this first round of compilation, C2 does the following:

  • From the expression b.byteValue() & 0xff, it draws the conclusion that x is between 0 and 255. Therefore, the tests for x == -1 cannot be true, and are optimized away.
  • At this point, the class java.lang.System still hasn't been loaded, so all the calls to System.out.println are replaced with traps that deoptimize the method and revert back into the interpreter awaiting class loading and recompilation.
  • To evaluate the b.byteValue() & 0xff expression, the compiler is smart enough to inline an unsigned memory byte load, to bring the value in from memory without having to do a following and operation on it, so that single instruction alone is supposed to fill the value of x, but this is where the compiler bug comes in and mistakenly replaces this with a signed load (a movsbl instruction). So x is actually loaded with complete sign extension to 32 bits, but the compiler has already assumed that the value of x be in the range 0-255.

What then happens is that the x == -1 test does not trigger since C2 has optimized it away, but the x != 255 test does trigger, because the compiler hasn't been smart enough to see that x should always be 255 (I do find this slightly strange since both Byte.valueOf() and b.byteValue() have been inlined, but apparently it is what it is). When it enters the following clause, the code attempts to load System.out, which promptly deoptimizes the code, loads the class, and finishes the iteration in the interpreter. This is why x is "properly" treated as -1 for the rest of this iteration.

Then the loop continues for another couple of thousand iterations in the interpreter and C1-compiled code until C2 kicks in again to reoptimize the code now with java.lang.System loaded. What then happens is the following:

  • Again, the tests for x == -1 are optimized away as impossible.
  • However, this time java.lang.System is loaded, and the calls to format x + " is not 255 (" + i + ")" are inlined. Looking at the source of Integer.toString(), this means that the test for output of negative integers is eliminated (again, as x is determined to be positive), and thus x is formatted as if it were a positive number of 1 digit (since it is less than 10). Therefore, this one digit is output as '0' + -1; that is, as /.

This might have been trivia at best, but it caught my mind. :)

Sign up to request clarification or add additional context in comments.

2 Comments

Nice workaround, but we decided to rollback to jdk1.8.0_11 instead and waiting for jdk1.8.0_40, thank you anyway.
@fei: If you want your actual question, "who can explain the output result", answered, see my edit. ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.