Boxing and Caches: Integer.valueOf(int) and friends

Recently, various folks have been discussing some code I wrote for the wrapper classes, java.lang.Integer and friends, back around March 2004 as part of JDK 5 beta 2:

private static class IntegerCache {
    private IntegerCache(){}

    static final Integer cache[] = new Integer[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
            cache[i] = new Integer(i - 128);
    }
}

public static Integer valueOf(int i) {
    final int offset = 128;
    if (i >= -128 && i <= 127) { // must cache 
        return IntegerCache.cache[i + offset];
    }
    return new Integer(i);
}

As correctly inferred, this caching code was added to support the object identity semantics of autoboxing for values between -128 and 127 (inclusive), as required by the language specification. Since the same object must be returned for integral values in this range, once the autoboxed value for, say, 0 is created, it must be retained until the virtual machine exits. That leaves the issue of whether or not the values in question are created eagerly or lazily and whether or not any container for those values is created eagerly or lazily.

The code above lazily creates the container object, but eagerly populates the object when it is created. This avoids having to use any synchronization when the elements are accessed. Of the three practical potential lazy-eager combinations, initially I planned to use the dual approach, eagerly creating an AtomicReferenceArray<Integer> container and then lazily populating this using compare and set. As I recall, Neal was first to suggest I use the initialize-on-demand holder class idiom to get lazy creation of the container object without synchronization. Further feedback from Josh and Doug Lea led to lazily allocating the plain array of Integer to be populated eagerly.

One reason I used the literal values "-128" and "127" in the code above is that those are the numbers given the specification and it is clear what they mean when used in the comparisons. Another code reviewer offered

if ((byte)i == i) {...} 

paired with IntegerCache.cache[((byte)x) & 0xff] to index into the array (using a different order) as an alternative to

if (i >= -128 && i <= 127) {...}

and IntegerCache.cache[i + offset]. The former approach does merit points for conciseness and cleverness, but it is more obscure and didn't run any faster in some quick micro-benchmarking so the more straightforward code remained.

Benchmarking this kind of caching code is difficult because what combination of lazy and eager allocation works best will be very dependent on the imposed workload. Therefore, something that always performs reasonably well is in some sense more robust than an algorithm that is really fast is some circumstances but much slower in others. In any case, I second the recommendation to use Integer.valueOf(foo) instead of new Integer(foo) since Integer.valueOf(foo) has the potential to use yet-to-be-developed fancy caching strategies while the constructor must always return a new object and object identity is rarely important for wrapper objects. Following Item 1 in Effective Java, if the wrapper classes could be redesigned, I would be inclined to remove the public constructors altogether and only provide factory methods.

Comments:

Ah, kewl, that was you was it? Well \*I\* like the code though I'm sure that if it were the biggest bottleneck in the JVM someone would find a tweak to make it work faster.

Rgds

Damon

Posted by Damon Hart-Davis on February 08, 2007 at 05:52 PM PST #

Thanks Joseph. It's great to see the background behind this little chunk of code and what alternatives were considered. I think I've learned a lot from looking even at this little piece of code and examining the alternatives. I agree especially with your comment at the end regarding removing the public constructors of the wrapper classes. Seems like that is often a better choice for immutable objects as a future hedge against better creation strategies. Alex Miller

Posted by Alex Miller on February 08, 2007 at 11:16 PM PST #

Hmm Alex, that's a good thought. In future, for all immutables, I will consider hiding the constructors and enforcing factory use...

Rgds

Damon

Posted by Damon Hart-Davis on February 09, 2007 at 12:09 AM PST #

Factories aren't just for immutables! Another advantage of factories is that any subtype of the return type, like an implementation-private class, can be returned.

Posted by Joe Darcy on February 09, 2007 at 05:47 AM PST #

Also a good thought, and I think I do that already in places when it seems like the RightThingToDo(tm) [each sold separately, batteries not included].

But I'm beginning to like the idea of NEVER exposing the constructors on new immutables without very good reason. There's a few bytecodes' code for the indirection, but I'm sure that HotSpot or ProGuard or whatever can take care of that semi-automatically.

Rgds

Damon

Posted by Damon Hart-Davis on February 09, 2007 at 06:26 AM PST #

Hmm, when I've got the next little JWS app out the door I feel a whole slew of exploratory refactoring coming on!

Posted by Damon Hart-Davis on February 09, 2007 at 06:29 AM PST #

Post a Comment:
Comments are closed for this entry.
About

darcy

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
News

No bookmarks in folder

Blogroll