How Strings live in memory: the string pool
Literals vs new String, how ldc and the constant pool feed the StringTable, == vs equals, compile-time constant folding, runtime concatenation, where the pool lives since Java 7, -XX:StringTableSize and how pooled strings are collected.
On this page
🧵 What is the string pool?
The string pool is a JVM-wide table of canonical String objects. Every string literal in your code, such as "PAID", resolves to the one pooled instance with that text, so a literal that appears in 300 classes still costs one object. Strings you build at runtime (from a database row, an HTTP header, "order-" + id) are ordinary heap objects and are not in the pool unless you ask for it.
For this lesson, we'll focus on what happens in memory when you write a literal versus new String(...), how the ldc bytecode and the class constant pool feed the pool, where the pool has lived since Java 7, which expressions the compiler folds into constants, and how pooled strings get garbage collected. This topic is asked in almost every Java screen because one line, new String("a"), lets an interviewer check whether you can picture references, objects and the heap at the same time.
Below the line (out of scope)
- Why
Stringis immutable in the first place: Why String is immutable - The everyday
==vsequals()rules: String equality intern()costs, deduplication and compact strings: Interning and compact strings- Building strings efficiently: StringBuilder vs StringBuffer
The 30-second answer
String literals are interned: the class file stores them in its constant pool, and the first time an ldc instruction loads one, the JVM looks the text up in a global StringTable and either reuses the existing String or creates and registers one. So two identical literals are the same object and == is true. new String("a") always allocates a fresh object on top of the pooled literal, so == is false while equals() is true. Compile-time constants like "a" + "b" or a final constant plus a literal are folded by javac and pooled; anything concatenated at runtime is a new, unpooled object. Since Java 7 the pool lives in the regular heap, its entries are weak, and unreachable pooled strings are garbage collected like anything else.
What you’ll learn
- Explain how a literal becomes a pooled
String:CONSTANT_String, the firstldcand the StringTable lookup - Answer **how many objects
new String("a")creates** precisely, including the sharedbyte[]on Java 9+ - Predict
==results using constant folding (JLS §15.29) versus runtime concatenation - Describe why the pool moved from PermGen to the heap in Java 7 and how pooled strings are garbage collected
- Inspect and tune the table with
-XX:StringTableSize,-XX:+PrintStringTableStatisticsandjcmd VM.stringtable
Interview radar
Asked very often: "How many objects does new String("a") create?" and "What do these == comparisons print?"
Senior rounds probe: How ldc resolves a CONSTANT_String, why the pool moved out of PermGen in Java 7, and whether pooled strings are ever collected.
Production angle: Code that compares strings with == passes tests built from literals, then fails on values read from JSON or a database.
01Picture it first: a shared dictionary on the office shelf
Think of the pool as one printed dictionary on a shared shelf. When you need the word "PAID", you don't print your own copy; you walk to the shelf and point at the page that already exists. Everyone who points at that page is pointing at the same physical paper. new String("PAID") is like photocopying the page anyway: same words, different sheet.
That gives you the two questions an interviewer is really asking. "Same sheet?" is ==. "Same words?" is equals(). Let's draw it.
The picture below shows three variables after String a = "order"; String b = "order"; String c = new String("order");.
Variables a and b point to the single pooled String for "order"; c points to a separate String created by new, and both String objects share one byte array
Notice three things.
aandbhold the same reference, because both literals resolve to the pooled object.cpoints to a secondStringobject that lives outside the pool.- Since Java 9, the
String(String original)constructor copies thevaluefield from the original, so both objects share onebyte[]. You pay for one extra 24-byteStringheader object, not a second copy of the text.
02How literals reach the pool: the constant pool, ldc and the StringTable
Let's get the mechanics right, because senior interviewers like to go one level below "literals are pooled".
Try it yourself first
Before reading on, guess what javap -c prints for this method. Which instructions create objects?
public class Order {
static String status() {
return "PAID";
}
static String copy() {
return new String("PAID");
}
}
Compile it with JDK 21 and run javap -c -v Order. The interesting parts look like this (constant pool indexes vary between compilers):
#7 = String #8 // PAID
#8 = Utf8 PAID
static java.lang.String status();
0: ldc #7 // String PAID
2: areturn
static java.lang.String copy();
0: new #9 // class java/lang/String
3: dup
4: ldc #7 // String PAID
6: invokespecial #11 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: areturn
Premium lesson
Keep reading with the Java Pass
The rest of this lesson is part of Java One-Stop Interview Prep: in-depth explanations, diagrams, model answers and how to say them in the interview.
- ==, equals() and constant folding
- Real world: where the pool lives and how it is collected
- Advanced: edge cases interviewers probe
- How to say it in the interview
From ₹199 · one-time payment · No auto-renewal
Further reading (10)
- JLS §3.10.5: String Literals
- JLS §15.29: Constant Expressions
- JLS §4.12.4: final Variables (constant variables)
- JVMS §5.1: The Run-Time Constant Pool
- JVMS §6.5: ldc
- String.intern() (Java SE 21 API)
- JDK-6962931: move interned strings out of the perm gen
- JEP 122: Remove the Permanent Generation
- JEP 280: Indify String Concatenation
- Baeldung: Guide to Java String Pool