16

I have this code (put aside its appropriateness for now):

    Class<?> cacheClass = Class.forName("java.lang.Integer$IntegerCache");
    Field cacheField = cacheClass.getDeclaredField("cache");
    cacheField.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(cacheField, cacheField.getModifiers() & ~Modifier.FINAL);

    Integer betterCache[] = new Integer[255];
    for (int i = 0; i < betterCache.length; i++) {
        betterCache[i] = 20;
    }
    cacheField.set(null,  betterCache);
    System.out.println(10);
    System.out.println((Integer) 10);

I expect the second println to print 20, as I replaced cached Integers with 20. When I debug program in Eclipse it does as I expect, it gets the value from the cache and prints 20, whereas it prints 10 in both cases when I just run it either from IDE or by invoking java. How can this behavior be explained?

UPD: It works this way if compiled with 1.8 javac. It prints 10 and 20 if compiled with 1.6 version.

6
  • You are printing 10 in both cases. So why should it print 20 ? Commented Jan 15, 2016 at 17:26
  • In the second case it's an object, so it calls Integer.valueOf method to print it, and in that method it takes values from the cache I just substituted with an array filled with 20. Commented Jan 15, 2016 at 17:29
  • 2
    Interesting problem (though you are playing with fire). Commented Jan 15, 2016 at 18:04
  • Have you tried with maven or java -jar ? I think some IDE's cache could cause this problem. Commented Jan 21, 2016 at 18:41
  • 1
    You may find the answer at Change final value compiled by JIT interesting too as you are dealing with final just like that question. Commented Jan 25, 2016 at 8:20

2 Answers 2

5
+200

This is definitely caused by Just in Time Compiler. You should add -XX:+PrintCompilation to JVM options, it is also more visible if you iterate

System.out.println((Integer) 10);

a lot of times. You will notice that compiling

java.lang.Integer::valueOf (32 bytes)

and

java.nio.ByteBuffer::arrayOffset (35 bytes) 

affects the result.

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

1 Comment

Do you mind explaining the output of -xx:+PrintCompilation... I went through it but really couldn't understand much...
2

EDIT

I was completely wrong

Definitely you are playing with fire, in my point of view, this is for a race conditions (unsafe thread in java 8). If you check this:

    Class<?> cacheClass = Class.forName("java.lang.Integer$IntegerCache");
    Field cacheField = cacheClass.getDeclaredField("cache");
    cacheField.setAccessible(true);
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setAccessible(true);
    modifiersField.setInt(cacheField, cacheField.getModifiers() & ~Modifier.FINAL);
    Integer firstCache[] = (Integer[])cacheField.get(null);
    Integer betterCache[] = new Integer[255];
    for (int i = 0; i < betterCache.length; i++) {
        betterCache[i] = 20;
    }
    System.out.println(firstCache == betterCache);
    cacheField.set(null, betterCache);
    System.out.println(10);
    for (int i = 0; i < 1000000; i++) {
        System.out.println((Integer) 10);     
    }

You'll see the Java burn.

6 Comments

Thanks for your answer. I still don't understand why changing 20 (basically call to valueOf) to creating new value changes this behavior. It does not seem it's evaluated in compile-time (though bytecode stuff is very unclear to me), as in the bytecode for both versions there is the same statement bipush 10 for the second println. Please also see update to my question
@cliffroot I don't know, but I can see differences with/without breakpoints In debug mode. I'm start to thinking in a race conditions.
Art Taylor: "I have two new rules. 1) If your application is slow, add caching. 2) If your application is buggy, remove caching." twitter.com/reeses/status/157344234339446784
lol. You know what's funny. I tried that loop for 100. Sometimes it writes down 20 as first value and then all 10, sometimes it doesn't (all 10)
@cliffroot when I tried with 1000000, It show 158, WTF!
|

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.