Question 1 · Section 12

How String Pool Works

In a typical Java application, strings make up 25-40% of all objects. Without the pool, each copy of "Hello" would create a separate object, even if the text is completely ident...

Language versions: English Russian Ukrainian

🟢 Junior Level

String Pool is a special mechanism in JVM that stores only one copy of each unique string. This helps save memory.

In a typical Java application, strings make up 25-40% of all objects. Without the pool, each copy of "Hello" would create a separate object, even if the text is completely identical.

When you create a string via a literal, JVM checks whether such a string already exists in the pool. If yes — it returns a reference to the existing object. If no — it creates a new one and adds it to the pool.

Example:

String s1 = "Hello";
String s2 = "Hello";
// s1 and s2 — this is the same object in memory!
System.out.println(s1 == s2); // true

Why this is needed: If every string were created as a separate object, the application would consume much more memory, especially if identical strings appear frequently.

When to use: Use string literals (String s = "value";) by default — JVM will place them in the pool automatically.


🟡 Middle Level

How it works

String Pool is implemented as a StringTable hash table inside JVM. When a class is loaded, all string literals from the class’s Constant Pool are automatically placed into the String Pool.

Constant Pool — a table in the .class file containing all literals, method names, and other class constants.

Two ways a string enters the pool:

  1. Literals — automatically when class loads: String s = "Hello";
  2. intern() method — manually at runtime: String s = new String("Hello").intern();

Practical application

String s1 = "Java";              // Literal → immediately in pool
String s2 = new String("Java");  // new → new object in heap (NOT in pool)
String s3 = s2.intern();         // intern() → returns reference from pool to s1

System.out.println(s1 == s2); // false (different objects)
System.out.println(s1 == s3); // true (same object in pool)

Typical mistakes

  1. Mistake: Using new String("literal") without need Solution: Use literals — String s = "literal";

  2. Mistake: Comparing strings via == instead of equals() Solution: Always use equals() for content comparison

Approach comparison

| Approach | In pool? | Creates object? | When to use | | ——————– | —————— | ——————– | —————————————- | | Literal "Hello" | Yes | Only if not in pool | 99% of cases | | new String("Hello")| No (literal — yes) | Always new | Practically never | | s.intern() | Yes | Only if not in pool | When working with huge amounts of duplicates |


🔴 Senior Level

Internal Implementation

String Pool is a native StringTable hash table with open addressing (method chaining). Keys and values are references to java.lang.String objects.

// Simplified structure from OpenJDK
class StringTable : public RehashableHashtable<oop, mtSymbol> {
  // oop — pointer to Java object
  // Uses hashing via String::hash_code
};

Key JVM parameters:

  • -XX:StringTableSize=N — hash table size (default 60013 in Java 8+, previously 1009)
  • Starting from JDK 11 (JEP 341), StringTable supports dynamic resizing when load factor is exceeded, similar to HashMap.

String Pool Memory Evolution

Java Version Location Problems
Java 6 and below PermGen Fixed size, frequent OOM: PermGen space
Java 7+ Java Heap Managed by GC, limited only by -Xmx
Java 8+ (Metaspace) Java Heap (not Metaspace!) Can still cause OOM: Java heap space

Common misconception: String Pool is in Metaspace. This is incorrect — it remained in Heap.

Architectural Trade-offs

Mass intern() usage:

Pros:

  • Drastic RAM reduction for duplicate strings
  • Fewer objects → fewer GC pauses

Cons:

  • intern() is a native call with hash computation and table lookup
  • With large number of strings, contention on global StringTable
  • If StringTableSize is small → long collision chains → O(n) degradation

Edge Cases

  1. StringTable collisions: If number of strings » StringTableSize, search degrades from O(1) to O(n). Check: jcmd <pid> VM.stringtable -verbose

  2. String Pool and GC: In Java 7+, pooled strings can be GC’d if unreferenced. But if you hold references — they will never be collected.

  3. Compact Strings (Java 9+): Don’t directly affect pool mechanism, but save 50% memory for Latin strings within the pool.

Performance

  • Lookup in empty pool: ~nanoseconds
  • Lookup in pool with 1M strings (proper StringTableSize): ~tens of nanoseconds
  • Lookup in pool with 1M strings (small StringTableSize): microseconds (collisions!)

Production Experience

When loading millions of records from DB (e.g., 1M users with country field), where only 200 unique countries exist:

  • Without intern(): 1M String objects → ~48MB
  • With intern(): 200 objects in pool + 1M references → ~5MB
  • But: CPU overhead on each intern() call can be 10-50%

Monitoring

# StringTable statistics
jcmd <pid> VM.stringtable -verbose

# Analysis via JOL
System.out.println(GraphLayout.parseInstance(stringTable).toFootprint());

Best Practices for Highload

  • Increase -XX:StringTableSize to a prime number > expected unique string count
  • Don’t use intern() for short-lived strings (they’ll die in Young Gen anyway)
  • Consider -XX:+UseStringDeduplication (G1 GC) as a transparent alternative

🎯 Interview Cheat Sheet

Must know:

  • String Pool — hash table (StringTable) in JVM, stores one copy of each unique string
  • Literals automatically enter the pool on class load
  • new String("...") creates a separate object in heap, NOT in pool
  • intern() adds string to pool and returns reference from pool
  • In Java 7+ String Pool is in Java Heap (not in PermGen/Metaspace!)
  • -XX:StringTableSize — hash table size (default 60013 in Java 8+)
  • With StringTable collisions, search degrades from O(1) to O(n)
  • Compact Strings (Java 9+) save 50% memory for Latin-1 strings in pool

Frequent follow-up questions:

  • Where is String Pool located? — In Java Heap (since Java 7). Common mistake — answering Metaspace.
  • How does a string enter the pool? — Via literals (automatically) or intern() call (manually).
  • Can a string be removed from the pool? — Yes, in Java 7+ GC can collect pool entries if unreferenced.
  • What happens with new String("Hello").intern()? — An object is created in Heap, then intern() finds "Hello" in pool and returns the pool reference. The Heap object becomes garbage.

Red flags (DON’T say):

  • ❌ “String Pool is in Metaspace” — incorrect, it’s in Heap
  • ❌ “== always works for strings” — only works for literals/interned strings
  • ❌ “intern() is free” — it’s a native call with CPU overhead
  • ❌ “String Pool has a fixed size” — it’s limited only by -Xmx in Java 7+

Related topics:

  • [[2. Difference Between Creating String via Literal and via new]]
  • [[3. When to Use intern()]]
  • [[11. Where is String Pool Stored (Which Memory Area)]]
  • [[12. Can String Pool Cause OutOfMemoryError]]
  • [[22. What is String Deduplication in G1 GC]]