How is the Integer cache implemented internally, and what is and isn't configurable about it?
answer
- private static IntegerCache: array low=-128..high
- valueOf indexes cache[i+128] in range else new
- low fixed, high raisable via -XX:AutoBoxCacheMax
- Long/Short/Byte cache -128..127 (no knob), Char 0..127
- Float/Double not cached
basics
~20 sInteger holds a static IntegerCache: an array of pre-built Integer objects for -128 up to a high bound (default 127). valueOf indexes into it. The low bound -128 is fixed; only the high bound is configurable via a JVM flag.
solid answer
~40 sInteger.IntegerCache is a private static nested class initialized at class load. It allocates an array of Integer instances covering low=-128 to a high bound that defaults to 127. Integer.valueOf(i) checks if i is within [low, high]; if so it returns cache[i + 128], otherwise it does new Integer(i). The array is built once and eagerly, so the small flyweights are shared process-wide. The low bound is hard-coded at -128 and cannot be changed. The high bound can be raised (never lowered below 127) via -XX:AutoBoxCacheMax=N or the property java.lang.Integer.IntegerCache.high; this lets you cache more values. Long/Short/Byte have a fixed -128..127 cache with no such knob; Character caches 0..127. Raising the cache only saves allocations — it must never be used to make == comparisons 'work', since that couples correctness to a JVM flag.
code
java · 18 lines// Conceptual sketch of the JDK's Integer.valueOf + IntegerCache
private static final class IntegerCache {
static final int low = -128; // fixed
static final int high; // default 127, raisable via -XX:AutoBoxCacheMax
static final Integer[] cache;
static {
int h = 127; // ... possibly overridden by java.lang.Integer.IntegerCache.high
high = h;
cache = new Integer[(high - low) + 1];
for (int k = 0, v = low; k < cache.length; k++, v++) cache[k] = new Integer(v);
}
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i - IntegerCache.low]; // shared flyweight
return new Integer(i); // fresh object
}go deeper
Knows there is a cache for small Integers and that it defaults to -128..127.
Can describe valueOf checking the range and returning cached vs new objects, and names other cached wrappers.
Explains the IntegerCache structure, the fixed-low/raisable-high asymmetry and the flag, which wrappers cache and which don't, and why the flag is a tuning—not correctness—lever.
Reasons about class-init eager allocation, startup-memory tradeoffs of widening the cache, governance around relying on it, and JVM-version portability of the behavior.
## The data structure Inside `java.lang.Integer` there is a **private static nested class** `IntegerCache`. 'Static nested class' means it belongs to `Integer` itself, not to any instance, and 'private' means only `Integer` can use it. It holds: - a `static final int low = -128;` - a `static final int high;` (computed at load time) - a `static final Integer[] cache;` — an array of pre-constructed `Integer` objects, one per value from `low` to `high` inclusive. When the `Integer` class is loaded, a **static initializer** runs once and fills the array: `cache[k]` holds an `Integer` for the value `low + k`. These are the **Flyweight** instances — built eagerly, shared for the life of the process. ## How valueOf uses it The logic of `Integer.valueOf(int i)` is essentially: ```java if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i - IntegerCache.low]; // shared instance return new Integer(i); // fresh object ``` The offset `i - low` (i.e. `i + 128`) maps the value to its array slot. Inside the range you always get the same object; outside it you always get a new one. Because autoboxing compiles to `valueOf`, this is the universal path for boxing literals and assignments. ## What is fixed vs configurable - **Low bound (-128): fixed.** The JLS requires `valueOf` to cache at least -128..127, and the implementation hard-codes `low = -128`. You cannot move it. - **High bound: configurable upward.** It defaults to 127 but can be raised with the JVM option `-XX:AutoBoxCacheMax=<N>` (HotSpot) or the system property `java.lang.Integer.IntegerCache.high`. The implementation takes `max(127, requested)`, so you can only **extend** the cache, never shrink it below 127. Raising it caches more values (e.g. up to 1000), trading a bit of startup memory for fewer allocations of those values. ## Other wrapper caches - `Long`, `Short`, `Byte`: cache **-128..127**, **not configurable**. - `Character`: caches **0..127**. - `Boolean`: `TRUE`/`FALSE` are effectively the only two instances. - `Float`, `Double`: **no cache** — every box is a new object (caching them is pointless since float identity rarely helps and equal float values are common but uniform sharing buys little). ## Why the high bound is the only safe lever — and even then, sparingly The cache exists purely for **memory/allocation efficiency**: real programs box the same small integers constantly. Raising `AutoBoxCacheMax` is a legitimate (rare) tuning move when profiling shows heavy boxing of values just above 127. It is **never** a correctness tool: code must not depend on `==` returning true because a value happens to be cached, because that ties program behavior to a JVM flag and breaks the moment the flag, the value range, or the JVM changes. Correctness for value comparison is always `equals()` / `Objects.equals()`. ## Practical senior takeaways - The flyweights are real, shared, immutable objects created at class init — so `Integer.valueOf` is allocation-free in range. - Know the asymmetry: low fixed, high raisable, and only for `Integer`. - Treat `AutoBoxCacheMax` as a niche performance knob, audited, not a behavioral dependency.
- If you set -XX:AutoBoxCacheMax=1000, does Integer a = 500; b = 500; a == b become true?Yes, because 500 is now in the cache and both refer to the same flyweight. But relying on this is an anti-pattern — it makes correctness depend on a JVM flag; still use equals().
- Why doesn't the JDK cache Double values like it caches Integer?Doubles span a vast, continuous range with little repetition of identical bit patterns, so a small fixed cache would rarely hit; the memory/lookup cost isn't justified, and NaN/+-0.0 identity semantics complicate sharing.
saying these in an interview costs you the question
- Claiming you can lower the cache below 127
- Saying Long/Short caches are configurable like Integer's
- Using AutoBoxCacheMax to make == 'work'
- Believing Float/Double are cached