Definition
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?
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:
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?
// 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:
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:
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.
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
}
}
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
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.
---