Caching in Wrapper Classes

yellow racing car with the words code performance

Here’s a fact that you may not know. When you auto-box integral primitives (byteshortint and long) to their respective wrapper classes (ByteShortInteger and Long), the wrapper classes cache all values from -128 to +127. These values are later used by the valueOf() methods to give better performance than using a constructor.

Auto-boxing

Let’s use an example of auto-boxing an int into an Integer.

Integer a = 33;

Behind the scenes when the compiler generates code to auto-box the int, it uses the static Integer.valueOf() method. The valueOf() method first checks the cache. If the value to be auto-boxed falls in the range -128 to +127, then it returns the previously-created cached Integer. Otherwise it creates a new Integer object. This yields better performance for commonly used values.

Example

Let’s look at a code snippet:

Integer a = 33;  // same as Integer a = Integer.valueOf(33);  
Integer b = 33;
Integer c = new Integer (33);
System.out.printf("a == b : %b%n", a==b);
System.out.printf("a == c : %b%n", a==c);
System.out.printf("a.equals(b) : %b%n", a.equals(b));
System.out.printf("a.equals(c) : %b%n", a.equals(c));

Comparing a and b with the == operator returns true, while a == c returns false.

Both a and b refer to the same object in the cache, while c refers to a new Integer object on the heap. Using the equals() method will always return true.

Now let’s change the values of ab and c to the following:

Integer a = 133;
Integer b = 133;
Integer c = new Integer (133);
System.out.printf("a == b : %b%n", a==b);
System.out.printf("a == c : %b%n", a==c);
System.out.printf("a.equals(b) : %b%n", a.equals(b));
System.out.printf("a.equals(c) : %b%n", a.equals(c));

Now a == b returns false because they refer to different objects. This is because their values are outside the range of the cache limits. New Integer objects have been created for each value when auto-boxed.

Changing the Cache Limit

We can change the upper cache limit, but not the lower limit. When starting the JVM, we can use either of two options (the xxx is the value you want for the upper limit):

java -Djava.lang.Integer.IntegerCache.high=xxx MyClass

or

java -XX:AutoBoxCacheMax=xxx MyClass

The -D option will always work, but the -XX: option is non-standard, and may not be supported by the compiler you are using.

Why do you have to know this?

There are practical implications of caching other than just performance. Not knowing about the cache can lead to unexpected behaviour when comparing wrapped integral numbers which may take you a lot of time and effort to track down. And as an added knowledge bonus, it’s also a common question in certification exams.

If you found this useful, please leave a comment. And sign up to get our weeklyJava tips.

Leave a Comment

Your email address will not be published. Required fields are marked *