← All posts
javajvmdebugging

What is Integer Caching in Java

why 100 == 100 but 200 != 200 in Java, and how to avoid this memory optimization trap.

5 min read
Table of Contents7 sections

Look at this Java code. It looks very simple, but it consistently tricks even some experienced Java developers:

Integer a = 100;
Integer b = 100;
System.out.println(a == b);

Prints True.

Integer x = 200;
Integer y = 200;
System.out.println(x == y);

Prints False.

How is this possible? If 100 == 100 is true, why does Java think 200 == 200 is false? It feels like something is broken in the language.

In reality, this isn’t a bug at all. It is an intentional memory optimization feature built deep into the Java Virtual Machine (JVM).

The Problem with ==

To understand this, we first need to remember how the double equals (==) operator works when dealing with objects.

When you use == to compare two objects in Java (like Integer variables), Java does not compare their actual numerical values. Instead, it compares their memory addresses. It is simply asking: “Do these two variables point to the exact same spot in memory?”

Normally, when you create two objects, Java allocates two separate spaces in memory for them. Even if both objects hold the value 200, they live at two different addresses (let’s call them Address A and Address B).

Because Address A is not the same as Address B, x == y returns false. That makes perfect logical sense.

Makes sense then why does this happen? why 100 == 100 return true? as per the above logic both the Integer objects should be stored at different memory addresses. How can == return true then?

The Java Integer Cache

To understand this, we have to go back in time to 2004, to the release of Java 5.

At the time, Java engineers noticed that developers used small numbers like 0, 1, 10, or 100 constantly. They were used everywhere as array indexes, loop counters, and default statuses.

Creating a brand new object in memory takes a tiny bit of processing time and space. Doing this millions of times for the same small numbers was causing unnecessary performance and memory problems in large applications.

To fix this, Java introduced the Integer Cache.

When the JVM starts up, before your program even begins to run, it automatically creates and caches an Integer object for every number from -128 to 127.

How Autoboxing Triggers the Cache

When you write Integer a = 100;, Java performs something called autoboxing. It automatically converts your primitive number into an Integer object. Behind the scenes, the code actually compiles to this:

Integer a = Integer.valueOf(100);

If we peek directly into the Java source code for that valueOf() method, we can see:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

The logic is simple:

  1. Is the number between -128 and 127?
  2. If yes, hand back the pre-made, cached object.
  3. If no, create a brand new object.

Because 100 falls inside the cache limits, both Integer a and Integer b are handed a reference to the exact same cached object. They share the exact same memory address, so a == b returns true.

However, 200 is outside the cache limits. When you ask for 200, Java creates two fresh, separate objects at different memory addresses. Therefore, x == y returns false.

How to Fix It

This feature is cause of some bugs in some real world projects. You might write an application and test it using small IDs (like 5 or 10), and all your tests pass. Then, you push it to production, and the moment a database ID hits 128, the app mysteriously crashes unexpectedly.

The Golden Rule: Never use == to compare objects in Java.

You should always use the .equals() method. For Integer objects, the .equals() method is designed to ignore memory addresses and strictly compare the underlying numerical values:

Integer x = 200;
Integer y = 200;
System.out.println(x.equals(y));

Prints true.

Two Bonus things to Watch Out For

Before you go refactoring your code, keep these two extra rules in mind:

1. Primitives are Safe

If you are comparing basic primitive int variables instead of Integer objects, the == operator works perfectly.

int x = 200;
int y = 200;
System.out.println(x == y); // Prints: true

Because primitives are raw values and not objects, there are no memory addresses or cache lookups involved.

2. Avoid the new Keyword

If you explicitly create an integer using the constructor, you bypass the cache entirely:

Integer a = new Integer(100);
Integer b = new Integer(100);
System.out.println(a == b); // Prints: false

By using new Integer(), you force Java to create a brand new spot in memory, missing out on all the performance benefits of the cache. This is such a bad practice that Java officially deprecated this constructor in Java 9, and marked it for future removal under JEP 390: Warnings for Value-Based Classes.

Comments

No comments yet — be the first!

Community Guidelines & Conduct

By posting comments, you agree to treat others with respect. Harmful, offensive, or abusive conduct — including harassment, hate speech, defamation, spam, or deliberately disruptive behaviour — will result in your content being removed and your account being permanently banned.

This website is a personal creative space. The site owner reserves the right to moderate, hide, or remove any submission and restrict platform access at their sole discretion.

For data collection details and terms of service, see our Privacy Policy and Terms of Service.