Back to String & String Pool
Q2Question 2 of 12

What is the String Pool and how does it decide reuse?

Key Concepts
  • The String Pool is a special memory area in the heap where unique String literals are stored
  • String literals are automatically interned: "hello" and "hello" share one object
  • new String("hello") creates a separate object on the heap, bypassing the pool
  • intern() manually adds a String to the pool or returns the existing pooled version
  • The pool saves memory when many identical strings exist (e.g., account types, status codes)
  • Since Java 7, the pool is in the main heap (not PermGen), so it benefits from GC

Definition

The String Pool is a heap area where identical String literals are stored once and shared. Literals are auto-pooled. new String() bypasses the pool. intern() adds manually.

String Pool mechanics:

  • Literals are auto-pooled. "SAV2001" at multiple locations in code shares one object.
  • new String() bypasses the pool. Creates a separate heap object even if the value exists in the pool.
  • intern() returns the pooled version. If the value is already pooled, returns that reference. If not, adds it.
  • Since Java 7: Pool is in the main heap, not PermGen. Eligible for GC.

The String Pool is like the bank's master directory of account numbers. When a new literal appears, the directory checks if it already has that number. If yes, it hands back the existing entry. If no, it creates a new entry. new String() is like printing a separate copy of the account number without checking the directory.

Understanding the Concepts

How does the pool work?

JAVA
String a = "SAV2001";         // checks pool → not found → creates and adds to pool
String b = "SAV2001";         // checks pool → found → returns same object
System.out.println(a == b);   // true (same pooled object)

String c = new String("SAV2001");  // creates NEW object on heap, bypasses pool
System.out.println(a == c);        // false (different objects)
System.out.println(a.equals(c));   // true (same content)

String d = c.intern();  // returns the pooled version
System.out.println(a == d);  // true (d points to the pooled object)

How many objects are created?

This is a classic interview question:

JAVA
String s = new String("hello");
// Creates up to 2 objects:
// 1. "hello" literal → added to pool (if not already there)
// 2. new String() → separate heap object

When is the pool useful?

JAVA
// Account types: "SAVINGS" appears 1 million times in logs
// Without pool: 1 million String objects
// With pool: 1 shared object

String type1 = "SAVINGS";  // pooled
String type2 = "SAVINGS";  // same pooled object
// Memory: one object, not two

What should a senior engineer know?

  • Pool size tunable: -XX:StringTableSize=60013 (prime number recommended).
  • -XX:+PrintStringTableStatistics shows pool usage at JVM shutdown.
  • Do not intern unique strings. Interning transaction IDs or timestamps fills the pool with unique entries that never match, wasting memory.
  • String deduplication (G1) is automatic and works on heap strings, not just literals.

What are common mistakes?

A frequent mistake is using == to compare String content instead of equals().

Wrong approach:

JAVA
package com.bank.service;

public class AccountValidator {
    public boolean validateAccountType(String type) {
        // WRONG: == compares references, not content
        if (type == "SAVINGS") {
            return true;
        }
        return false;
    }
}

Correct approach:

JAVA
package com.bank.service;

public class AccountValidator {
    public boolean validateAccountType(String type) {
        // CORRECT: equals() compares content
        if ("SAVINGS".equals(type)) {  // null-safe: literal first
            return true;
        }
        return false;
    }
}

Using the literal on the left side of equals() prevents NullPointerException when the variable is null. This is a standard practice at National Bank of India for all string comparisons in service code.

What are the edge cases?

String behavior has several surprising edge cases that can catch banking developers off guard.

JAVA
package com.bank.edge;

public class StringEdgeCases {
    public static void main(String[] args) {
        // Edge case 1: compile-time constant folding
        String a = "SAV" + "2001";       // Compile-time constant
        String b = "SAV2001";
        System.out.println(a == b);       // true (same pool entry)

        // Edge case 2: variable concatenation is NOT pooled
        String prefix = "SAV";
        String c = prefix + "2001";       // Runtime concatenation
        System.out.println(b == c);       // false (different objects)

        // Edge case 3: final variable IS a compile-time constant
        final String finalPrefix = "SAV";
        String d = finalPrefix + "2001";  // Compile-time constant
        System.out.println(b == d);       // true (same pool entry)

        // Edge case 4: substring no longer shares char array (Java 7u6+)
        String full = "AccountNumber:SAV2001";
        String sub = full.substring(14); // New String, new char array
    }
}
TEXT
true
false
true

The final keyword makes the variable a compile-time constant, which changes how the compiler handles concatenation.

What are common interview follow-up questions?

Interviewers often follow up with these related questions:

  • How does this concept apply to a high-throughput banking transaction system?
  • What are the performance implications in a production environment?
  • How would you debug issues related to this concept?
  • What tools would you use to monitor this in production?
  • How does this behave differently in Java 8 vs Java 17+?

When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.

Visual Overview

DIAGRAM
  String a = "SAV2001";     → pool lookup → miss → create + add to pool → return
  String b = "SAV2001";     → pool lookup → hit  → return same object
  String c = new String("SAV2001"); → new heap object (pool not used for the new object)
  String d = c.intern();   → pool lookup → hit  → return pooled object

  a == b   → true  (same pooled object)
  a == c   → false (different objects)
  a == d   → true  (d is the pooled object)
  a.equals(c) → true (same content)

Key Takeaway

The String Pool stores unique String literals in a special heap area for sharing. Identical literals return the same object, saving memory. new String() creates a separate heap object. intern() returns the pooled version.

The pool is most effective for repeated values like account types, status codes, and branch names. Do not intern unique values like transaction IDs.

One sentence to remember: literals are auto-pooled and shared, new String() bypasses the pool, intern() returns the pooled version.

---

Share
Use ← → arrow keys to navigate