skip to content

What is the Java String pool (intern pool), and how does it affect == comparisons between strings?

level: middleimportance: should knowfreq 70%

answer

  1. Pool = cache of unique String objects
  2. Literals are auto-interned → == true
  3. new String / runtime concat → not pooled
  4. intern() returns the canonical pooled instance
  5. Pool optimizes memory, not a reason to use ==

basics

~20 s

The string pool is a cache of String objects that Java reuses for identical string literals. Because identical literals share one pooled object, == returns true for them. Strings built at runtime are not pooled, so == returns false for them.

solid answer

~50 s

The string pool (or intern pool) is a runtime cache of unique String objects. When the compiler sees a string literal, that literal is automatically interned: all identical literals across the program refer to the same pooled object, so == between them is true. Strings created with new String("...") or built at runtime (concatenation of variables, input, substring) are distinct objects on the heap and are not pooled unless you call intern(). intern() returns the canonical pooled instance for a given content, adding it to the pool if absent. Pooling exists to save memory and speed up literal comparison, but it makes == an unreliable equality test for content. The practical takeaway: never use == to test string equality. Use equals(). Excessive manual interning of many distinct strings can bloat the pool and add lookup cost, so intern() is for narrow deduplication cases, not a general tactic.

code

java · 14 lines
java
String lit1 = "hello";
String lit2 = "hello";
System.out.println(lit1 == lit2); // true  (shared pooled literal)

String runtime = new String("hello");
System.out.println(lit1 == runtime);          // false (separate object)
System.out.println(lit1 == runtime.intern()); // true  (intern returns pooled instance)

String folded = "hel" + "lo";        // compile-time constant → folded to literal
System.out.println(lit1 == folded);  // true

String x = "lo";
String concat = "hel" + x;           // runtime concatenation → not pooled
System.out.println(lit1 == concat);  // false

go deeper

for a junior

Knows literals are reused so == can be true for them, and that the safe choice is still equals().

for a middle

Explains automatic interning of literals, compile-time constant folding vs runtime concatenation, and what new String/intern() do to pooling.

for a senior

Discusses pool implementation (heap-based hash table in modern HotSpot), GC behavior, and when intern() is a worthwhile dedup optimization vs a memory/perf risk.

for a principal

Reasons about pool sizing and interning strategy at scale, cross-JVM portability of pooling assumptions, and sets guidance that correctness must never depend on interning (== for content is banned).

## The problem the pool solves Strings are everywhere in a program and many are identical (think of the literal `""`, `"true"`, field names, etc.). Allocating a separate object for every occurrence would waste memory. So the JVM maintains a **string pool** (also called the *string intern pool* or *string constant pool*): a table of unique `String` instances keyed by their content. ## How literals get pooled automatically A **string literal** is a string written directly in source code with double quotes, e.g. `"hello"`. At class-loading time, every literal is *interned*: the JVM checks whether a string with that exact content already exists in the pool. If yes, the literal reuses it; if not, it adds one. The result is that **all identical literals in your program point to the same object**: ```java String a = "hello"; String b = "hello"; // a and b are the SAME object System.out.println(a == b); // true ``` That single `true` is the entire reason `==` *appears* to compare text. It does not — it compares references — but for pooled literals the references are identical. ## What is NOT pooled Anything produced at runtime is a brand-new object unless you intern it: ```java String a = "hello"; String b = new String("hello"); // explicit new object, not pooled String c = "hel" + someVariable; // built at runtime, not pooled System.out.println(a == b); // false System.out.println(a == c); // false (even if c's content is "hello") ``` Note a subtlety: `"hel" + "lo"` where *both* sides are compile-time constants IS folded by the compiler into the single literal `"hello"` and therefore pooled. But `"hel" + variable` is computed at runtime and is not. ## intern() `String.intern()` lets you opt a runtime string into the pool. It returns the canonical pooled instance for that content — adding the string to the pool if no equal one exists yet: ```java String a = "hello"; String b = new String("hello").intern(); System.out.println(a == b); // true — b was replaced by the pooled instance ``` In modern HotSpot the pool is a hash table on the heap (it moved out of the fixed-size PermGen long ago), so over-interning many distinct strings costs heap and hash-table lookup time. Use `intern()` only as a deliberate deduplication optimization on a bounded set of frequently repeated strings — not as a way to enable `==`. ## Why this matters for equality The pool is precisely what makes `==` a *trap* for strings. `==` compares references; pooling sometimes makes references coincide; so `==` can pass for literals and silently fail for runtime strings of the same text. The correct, content-based test is always `equals()` (or `equalsIgnoreCase()`), regardless of pooling. ## Memory and lifecycle notes - Pooled literals live as long as their interning is reachable; the pool participates in normal GC for interned runtime strings in modern JVMs. - The pool is JVM-wide (shared across the application), not per-class. - Identity-based tricks using the pool are fragile across JVM versions; treat the pool as an optimization detail, never as a correctness guarantee.

  • Does "a" + "b" written as two literals create a pooled string?
    Yes. When both operands are compile-time constants the compiler folds them into a single literal ("ab"), which is interned and pooled. Only runtime concatenation (with a variable) produces a non-pooled object.
  • When is calling intern() actually a good idea?
    When you have a large number of duplicate runtime strings (e.g. parsed tokens, repeated keys) and want to collapse them to one instance to save memory. It is a targeted dedup optimization, not a general practice, since over-interning costs heap and lookup time.

The pool is like a shared library of reference books: every reader who quotes the same printed title is handed the one library copy (same object). If you photocopy your own (new String), you hold a separate copy even though the text matches.

saying these in an interview costs you the question

  • Thinking the pool makes == a valid content comparison
  • Assuming new String("x") is pooled
  • Believing all concatenations are folded (runtime concat is not)
  • Recommending intern() broadly to enable == comparisons

context