Here’s a fact that you may not know. When you auto-box integral primitives (byte
, short
, int
and long
) to their respective wrapper classes (Byte
, Short
, Integer
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 to wrapper classes
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 a
, b
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 need to know about this caching?
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.