In Java, Strings are one of the most frequently used data types. Because Strings are immutable and widely used, the JVM uses a special optimization called the String Pool. By interning Strings, Java reduces memory usage and improves performance when dealing with repeated values.
This tutorial explains how the String Pool works inside the JVM, how interning is implemented, and best practices for using it effectively.
Why the String Pool Matters
- Reduces duplicate string objects in memory.
- Improves performance by reusing immutable values.
- Ensures faster equality checks using reference comparison.
- Helps in large applications (web servers, databases, microservices) where string duplication is common.
Analogy: Think of the String Pool as a dictionary in a library. Instead of printing new copies for every reader, the JVM stores one reference copy and shares it across all readers.
What is the String Pool?
The String Pool (intern pool) is a special memory area in the JVM that stores unique String literals. When a new String is created, the JVM checks the pool first:
- If the String exists, the reference is reused.
- If not, the String is added to the pool.
Example
public class StringPoolDemo {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2); // true (pooled)
System.out.println(s1 == s3); // false (new object on heap)
}
}
s1
ands2
point to the same object in the String Pool.s3
creates a new object in the heap, even though it has the same content.
Interning Strings
The intern()
method explicitly adds a String to the pool.
String s1 = new String("Java");
String s2 = s1.intern();
System.out.println(s1 == s2); // false
System.out.println("Java" == s2); // true
intern()
ensures that only one copy of"Java"
exists in the pool.- Useful for reducing memory when dealing with many repeated Strings.
Evolution of String Pool in the JVM
- Java 6 and earlier → String Pool stored in PermGen.
- Java 7+ → Moved to the heap for better flexibility.
- Java 8+ → Further optimized with dynamic resizing.
- Java 11+ → Compact Strings introduced (byte[] instead of char[] for Latin-1).
String Pool and Garbage Collection
- Pooled Strings live as long as they are referenced.
- If an interned String is no longer referenced, it may be garbage collected.
- Large pools can contribute to memory pressure if not managed properly.
Real-World Use Cases
- Compilers and Parsers → Intern keywords for efficiency.
- Databases → Reuse SQL query templates.
- Logging frameworks → Reduce duplicate log message objects.
- Big data systems → Intern column names or schema identifiers.
Pitfalls of String Pooling
- Excessive interning can cause memory bloat.
- Unnecessary calls to
intern()
add overhead. - Large dynamic strings (e.g., JSON, XML) are not good candidates for pooling.
- May lead to
OutOfMemoryError: Java heap space
if pool grows too large.
JVM Tuning for String Pool
-XX:StringTableSize=<size>
→ Controls hash table size for pooled Strings.- Use tools like JFR (Java Flight Recorder) or JMC (Java Mission Control) to monitor string usage.
- Analyze heap dumps with VisualVM or Eclipse MAT.
Code Example: Performance with Interning
public class InterningPerformance {
public static void main(String[] args) {
long start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
String s = ("User" + i).intern();
}
long end = System.nanoTime();
System.out.println("Time taken: " + (end - start) / 1_000_000 + " ms");
}
}
Interning avoids duplicate Strings for "User0", "User1", ...
and improves memory efficiency.
JVM Version Tracker
- Java 6 → Pool in PermGen, limited size.
- Java 7 → Pool moved to heap, dynamically managed.
- Java 8 → Compact Strings optimization.
- Java 11+ → Continued optimizations for memory efficiency.
- Java 21+ → Enhanced performance in String operations with Project Lilliput.
Best Practices
- Use String literals where possible.
- Call
intern()
only when beneficial (e.g., high repetition). - Avoid interning large dynamically created strings.
- Profile string usage in production before tuning.
- Combine with modern GC (G1, ZGC) for efficient memory handling.
Conclusion & Key Takeaways
- The String Pool reduces memory by reusing immutable strings.
- Interning provides better performance for frequently repeated values.
- Overusing interning can backfire with memory bloat.
- JVM optimizations since Java 7 make pooling more efficient.
- Understanding String Pool is crucial for writing memory-optimized Java apps.
FAQs
1. What is the JVM memory model and why does it matter?
It defines how threads and memory interact, ensuring safety and performance.
2. How does G1 GC differ from CMS?
G1 uses region-based collection with predictable pauses, CMS had fragmentation issues.
3. When should I use ZGC or Shenandoah?
When low GC pause times are critical for system responsiveness.
4. What are JVM safepoints?
Points where JVM halts all threads for GC or JIT optimizations.
5. How do I solve OutOfMemoryError due to String Pool?
Tune -XX:StringTableSize
and reduce unnecessary interning.
6. How does JIT compilation optimize String usage?
Inlining and constant folding optimize repeated String operations.
7. Can interned Strings be garbage collected?
Yes, if they are no longer referenced in the application.
8. How do I monitor String Pool usage?
Use jcmd VM.stringtable
or tools like JFR/JMC.
9. What’s new in Java 11+ for Strings?
Compact Strings reduce memory usage by using byte[]
instead of char[]
.
10. How does GC differ in microservices vs monoliths?
Microservices prioritize startup and memory efficiency, monoliths tune for throughput.