Deep Dive into Java String Memory Management
If you’ve written any Java code, chances are you’ve worked with Strings. But have you ever wondered how Strings are stored in memory, or why "Hello" == "Hello" is sometimes true and sometimes false?
Let’s break it down step by step — with code, diagrams, and JVM internals — so you’ll never be confused again.
Strings in Java
- Strings in Java are immutable. Once created, they cannot be changed.
- JVM manages Strings in two main areas:
- String Pool (String Constant Pool, SCP) → stores unique literals, reused across the program.
- Heap memory (outside the pool) → stores
new String(...)objects.
This design makes Strings memory efficient and thread-safe.
Example Code
public class StringMemoryExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
String s4 = s3.intern();
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1 == s4); // true
}
}Step-by-Step Execution in JVM
Get Sathiswaran’s stories in your inbox
Join Medium for free to get updates from this writer.
Let’s see what happens behind the scenes for each line.
Step 1: String s1 = "Hello";
- JVM checks the String Pool.
"Hello"not found → creates a new pooled object.s1points to it.
Pool: "Hello" ← s1Step 2: String s2 = "Hello";
- JVM checks pool again.
"Hello"already exists → reuses the same object.s2points to the same reference.
Pool: "Hello" ← s1, s2✅ s1 == s2 → true
Step 3: String s3 = new String("Hello");
new String(...)forces a new object in heap memory.- Even though
"Hello"comes from the pool, a copy is made.
Pool: "Hello" ← s1, s2
Heap: new "Hello" ← s3❌ s1 == s3 → false (different objects)
✅ s1.equals(s3) → true (same characters)
Step 4: String s4 = s3.intern();
intern()checks the pool:"Hello"already exists.- Returns the pooled reference.
s4now points to the same object ass1ands2.
Pool: "Hello" ← s1, s2, s4
Heap: new "Hello" ← s3✅ s1 == s4 → true
❌ s3 == s4 → false
Why this design?
- Immutability → Safe to share Strings across threads.
- Pooling → Saves memory by reusing common values.
- Interning → Lets you control canonical representations (useful in large datasets).
Extra Nuances You Should Know
Compile-time constants are pooled
String a = "He" + "llo"; // optimized to "Hello"
String b = "Hello";
System.out.println(a == b); // trueRuntime concatenation is not pooled
String x = "He";
String y = x + "llo"; // runtime build → new object
System.out.println(y == "Hello"); // false
System.out.println(y.intern() == "Hello"); // truenew String("Hello") always creates a new object
→ Avoid unless you explicitly need a distinct reference.
Java 9+ uses Compact Strings
Strings internally store characters in a byte[] (Latin-1 or UTF-16) for better memory use.
Mental Picture (All Steps Together)
Step 1: s1 = "Hello"
Pool: ["Hello"] ← s1
Step 2: s2 = "Hello"
Pool: ["Hello"] ← s1, s2
Step 3: s3 = new String("Hello")
Pool: ["Hello"] ← s1, s2
Heap: [new "Hello"] ← s3
Step 4: s4 = s3.intern()
Pool: ["Hello"] ← s1, s2, s4
Heap: [new "Hello"] ← s3Key Takeaways
"Hello"(literal) → goes to the pool.new String("Hello")→ creates a new object on the heap.intern()→ ensures you get the pooled reference.- Use literals for efficiency; use
intern()when managing duplicates in large datasets.






