Back to Blog
Java

Understanding the Java String Pool

java string pool: Explains how the JVM string pool works, when strings are pooled, how intern() affects memory, and common pitfalls with string comparison.

JVMString InterningMemory ManagementJava PerformanceGarbage Collection
Diagram showing multiple string references pointing to a single pooled string object in the JVM heap

The java string pool is a runtime storage area inside the JVM that holds string literals so that identical text values can share the same object reference. When your code declares String name = "order"; in two different places, the JVM does not create two separate String instances. It resolves both references to the same pooled object, which reduces memory usage and makes equality checks faster.

How String Literals Enter the Pool

When the JVM loads a class, it processes the constant pool of that class file. String literals that appear in the bytecode are candidates for the string pool. The first time a literal is encountered, the JVM creates a String object and places it in the pool. Any subsequent reference to the same literal reuses that object.

String first = "order"; String second = "order"; System.out.println(first == second); // true

The == comparison returns true because both variables point to the same pooled instance. This is a common interview question, but it also has real operational meaning: pooled strings reduce heap pressure when the same text value appears across many objects.

When Strings Are Not Pooled

Not every String ends up in the pool. Strings created with the new keyword are ordinary heap objects, even if their content matches a pooled literal.

String pooled = "order"; String fresh = new String("order"); System.out.println(pooled == fresh); // false System.out.println(pooled.equals(fresh)); // true

The constructor new String("order") still evaluates the literal "order" for the constructor argument, so that literal is pooled. But the resulting String object is a separate heap instance. The == comparison fails because the references differ. The equals method returns true because the character sequences match.

This distinction matters when you are comparing strings in code that mixes literals with dynamically constructed values. Relying on == for string comparison is only safe when you know both sides are guaranteed to be pooled references.

String Interning with intern()

The intern() method explicitly places a string into the pool and returns the pooled reference. If the pool already contains a string with the same content, intern() returns that existing object instead of creating a duplicate.

String dynamic = new String("order"); String interned = dynamic.intern(); System.out.println(interned == "order"); // true

This is useful when you receive strings from external sources—parsed files, network responses, database results—and you expect many repeated values. Interning lets those repeated values collapse to a single object.

Interning is not free. The JVM must perform a lookup in the pool for every intern() call, and the pool itself consumes memory. The tradeoff is only worthwhile when the same content appears frequently and the strings are long-lived. Interning short-lived or unique strings can increase memory usage because the pooled objects are not garbage collected while the pool itself remains.

Pool Location and Garbage Collection

Before Java 7, the string pool lived in the permanent generation (PermGen), a separate memory area with a fixed size. This caused OutOfMemoryError: PermGen space in applications that pooled many strings. Since Java 7, the pool is part of the regular heap, so pooled strings are subject to normal garbage collection when they are no longer referenced.

This change matters operationally. In modern JVMs, pooled strings that are no longer reachable can be collected, which means the pool does not grow without bound in long-running applications. However, the pool is still a hash table internally, and a very large pool can add memory overhead beyond the string contents themselves.

Comparing Strings Safely

The existence of the pool does not change the rule that string comparison should use equals unless you have a specific reason to rely on reference identity.

String a = buildFromInput(); String b = buildFromInput(); if (a == b) { // unreliable unless both are interned }

If you control both sides and both are literals or explicitly interned, == is a valid optimization because it avoids a character-by-character scan. But in most application code, the safer and clearer choice is equals. The performance gain from == on pooled strings is rarely significant compared with the risk of a subtle bug when one side is not pooled.

Common Mistakes with the Pool

A frequent mistake is assuming that concatenation results are pooled. The compiler pools string literals that are constant expressions, but runtime concatenation produces a new object.

String part1 = "or"; String part2 = "der"; String combined = part1 + part2; System.out.println(combined == "order"); // false

Because part1 and part2 are variables, the concatenation happens at runtime and produces a new String instance. If the values were declared as final constants, the compiler would fold them into a single literal and the result would be pooled.

Another mistake is using intern() on every string in a hot path without measuring. The lookup cost and the memory retained by the pool can outweigh the benefit when the input has high cardinality. Interning is a deliberate memory optimization, not a default strategy.

Pool Behavior in Practice

For most applications, the string pool works quietly in the background. You do not need to call intern() or reason about pooled references in everyday code. The pool becomes relevant when you profile memory usage and see many duplicate string instances, or when you are writing code that performs a large number of string equality checks.

If duplicate strings dominate your heap, consider whether interning the values at the point of creation reduces the footprint. If you are comparing strings in performance-sensitive loops, verify that both operands are pooled before using ==. In all other cases, treat strings as ordinary objects and use equals for comparison.

The behavior of the pool is consistent across modern JVMs, but the exact internal implementation—hash table size, growth policy, and interaction with garbage collection—is not part of the Java specification. Do not write code that depends on those internals.

java string pool: Practical Usage and Code Examples | RYUSLOG DEV