RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Java questions
HardJava#247 min readJul 11, 2026

How does intern() work?

practice
learning
Practice modeTest yourself instead of reading straight through

Question: How does intern() work?

Answer: intern() asks the JVM for the canonical version of a string, meaning the one shared instance the runtime keeps for that text. If an equal string is already in the string pool, Java returns that existing object; otherwise, it adds the string to the pool and returns it. This is why == can become true for interned strings even though it usually should not be used for string content checks.

Interview-Ready Answer: In Java, intern() returns the pooled, canonical String for the same character sequence. If the pool already contains that text, I get the existing reference; if not, the JVM stores it in the pool and returns it. The practical payoff is that identical strings can share one object, but I should still use equals() for normal content comparison because intern() has overhead and is only worth using in special memory or identity-based cases.

🧠 Memory Map
Memory map — visual summary of this topic

What it is

Detailed Explanation: The string pool is a JVM-managed cache of string values. A canonical string is the one shared object chosen to represent a particular sequence of characters. intern() is the method that asks for that shared object.

How it works under the hood

  1. The JVM receives a call like s.intern().
  2. It looks up the string’s characters in the string pool, which is backed by a hash table called the StringTable in HotSpot.
  3. If an equal string is already present, the JVM returns that existing reference.
  4. If no equal string exists, the JVM inserts the string into the pool and returns it.
  5. After that, future equal strings can reuse the same pooled reference, which can reduce duplicate objects.

When and why to use it

You use intern() when you have many repeated strings and you care about memory or identity sharing. A classic example is a parser, tokenizer, compiler, or dedup-heavy domain where the same values repeat thousands or millions of times. In everyday business code, it is usually unnecessary because equals() is enough and simpler.

ApproachWhat you getBest use
equals()Content checkNormal string comparison
==Same objectOnly identity tests
intern()Pooled shared objectHeavy duplication cases

Version differences that matter

  • In older JVMs, the string pool lived in PermGen (permanent generation), a special memory area that could overflow.
  • Since Java 7, the pool lives on the regular heap, which made interned strings easier to manage and less likely to hit PermGen issues.
  • That change is a common interview point: the concept stayed the same, but the memory location changed.

Performance and trade-offs

Lookup is typically close to O(1) average time because the pool uses hashing, but it is not free. Interning has extra work: hashing, lookup, possible insertion, and more pressure on the pool. Space can improve if many duplicates collapse into one object, but if most strings are unique, intern() can waste time with little gain.

Important edge cases

  • null does not have an intern() method call in practice; calling it on a null reference throws NullPointerException.
  • Interning does not change the characters in the string; it only changes which object reference you get back.
  • Two strings with the same text may still be different objects until both are interned or created from the same literal.
  • Never rely on intern() to make all string comparisons faster. It can actually slow code down if overused.

Memory Hook: Think of the string pool like a library of name tags: intern() asks, “Do we already have this exact name tag?” If yes, reuse it; if not, print one and file it for later.

Real-World Story: Imagine a log ingestion service for a large chat app. Every message has repeated values like country codes, event types, and feature flags. The team interns a small set of high-repeat strings so that millions of identical values do not create millions of separate objects, which helps reduce heap usage and GC work.

What goes wrong when someone misunderstands it? A developer uses == on non-interned strings and gets random-looking false results, so messages are misrouted or deduplication fails. In production, you might see logs like routing key mismatch, repeated database inserts, or memory growth because the code interns too many unique user-generated strings. Users notice delays, and the GC logs show more frequent pauses because the heap is full of strings that should never have been interned in the first place.

Java
import java.util.Objects;

public class InternDemo {
    public static void main(String[] args) {
        // String literals are already in the string pool.
        String literal = "hello";

        // new String(...) creates a distinct object on the heap, even if the text is the same.
        String heapString = new String("hello");

        // intern() asks the JVM for the pooled, canonical version.
        String pooled = heapString.intern();

        System.out.println("literal == heapString      : " + (literal == heapString));
        System.out.println("literal == pooled          : " + (literal == pooled));
        System.out.println("heapString.equals(literal)  : " + heapString.equals(literal));

        // Dynamic strings are a common reason to consider interning.
        String part1 = new String("he");
        String part2 = "llo";
        String dynamic = (part1 + part2); // built at runtime, so not automatically pooled
        String dynamicInterned = dynamic.intern();

        System.out.println("dynamic.equals(literal)    : " + dynamic.equals(literal));
        System.out.println("dynamic == literal         : " + (dynamic == literal));
        System.out.println("dynamicInterned == literal  : " + (dynamicInterned == literal));

        // Edge case: calling a method on null throws NullPointerException.
        try {
            String nullRef = null;
            nullRef.intern();
        } catch (NullPointerException ex) {
            System.out.println("null.intern() throws NPE as expected: " + ex.getClass().getSimpleName());
        }

        // Defensive check if you accept user input.
        String maybeNull = null;
        String safe = internOrNull(maybeNull);
        System.out.println("safe value is null          : " + (safe == null));
    }

    private static String internOrNull(String value) {
        // This helper shows the right pattern when null is possible.
        return value == null ? null : value.intern();
    }
}

Follow-up & Tricky Questions:

  • What is the difference between equals() and == for strings?
    equals() compares text content; == compares object identity. Interning can make == appear to work, but only because both references point to the same pooled object.
  • Does intern() create a new string?
    Sometimes yes, sometimes no. If the pool already has the string, Java returns the existing object; otherwise, the current string is added to the pool and that reference is returned.
  • When should you avoid intern()?
    Avoid it for lots of unique, one-off strings, because the lookup and pool management cost can outweigh any memory savings. Normal application code is usually better off with plain equals().
  • Where is the string pool stored in modern Java?
    Since Java 7, it is on the heap. Older JVMs stored it in PermGen, which is why older articles mention PermGen overflows.
  • Is intern() thread-safe?
    The JVM manages the pool safely, so callers do not need to synchronize around it. That said, thread-safe does not mean free; it can still be a bottleneck if overused.
  • Can interned strings be garbage-collected?
    In modern JVMs, the pool is on the heap, so GC behavior is different from old PermGen-era advice. The practical takeaway is still to intern only when you have a clear reuse benefit.
  • Does intern() change the string’s content?
    No. It only changes which shared object reference you get back; the characters stay exactly the same.
  • Can intern() fix bad string comparison code?
    No, it only hides the symptom sometimes. The correct fix is to use equals() for value comparison and reserve == for identity checks.
  • Does intern() guarantee the same reference for all equal strings?
    Only after the strings are interned. Equal ordinary strings can still be different objects until they pass through the pool.
  • Is it okay to intern user input?
    Usually no, because user input can be very high-cardinality and unique. That can grow the pool and waste memory without meaningful reuse.
  • Does interning speed up comparisons?
    It can make some identity checks fast, but the interning step itself has a cost. For most code, equals() is already efficient and much clearer.
  • What happens if two equal strings are interned from different threads?
    The JVM ensures only one canonical pooled instance is used. Both callers end up with the same shared reference.

Common Mistakes:

  • Using == for normal comparison. Correction: use equals() unless you specifically want object identity.
  • Thinking intern() always improves performance. Correction: it adds work and only helps when many equal strings repeat.
  • Interning everything, including user input. Correction: that can bloat the pool with one-time values.
  • Forgetting the Java 7 pool change. Correction: modern Java stores the pool on the heap, not PermGen.

Memory Hook: “intern() is the librarian.” It checks whether the same book title already exists on the shelf; if it does, you borrow that copy, and if not, the librarian adds one for everyone to share.

Cheat Sheet:

  • intern() returns the canonical pooled string.
  • Equal text + interned = same reference.
  • equals() checks content; == checks identity.
  • Since Java 7, the string pool is on the heap.
  • Use intern() only for repeated, high-duplication strings.
  • Avoid interning lots of unique input values.

Practice Tasks:

  • Write three strings with the same text using a literal, new String(), and concatenation, then print == and equals() results before and after intern().
  • Modify the demo to accept a string from the command line and intern it safely only when it is not null.
  • Measure a loop that interns repeated values versus unique values, and observe when memory savings are worth the overhead.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

import java.util.Objects; public class InternDemo { public static void main(String[] args) { // String literals are already in the string pool. String literal = "hello"; // new String(...) creates a distinct object on the heap, even if the text is the same. String heapString = new String("hello"); // intern() asks the JVM for the pooled, canonical version. String pooled = heapString.intern(); System.out.println("literal == heapString : " + (literal == heapString)); System.out.println("literal == pooled : " + (literal == pooled)); System.out.println("heapString.equals(literal) : " + heapString.equals(literal)); // Dynamic strings are a common reason to consider interning. String part1 = new String("he"); String part2 = "llo"; String dynamic = (part1 + part2); // built at runtime, so not automatically pooled String dynamicInterned = dynamic.intern(); System.out.println("dynamic.equals(literal) : " + dynamic.equals(literal)); System.out.println("dynamic == literal : " + (dynamic == literal)); System.out.println("dynamicInterned == literal : " + (dynamicInterned == literal)); // Edge case: calling a method on null throws NullPointerException. try { String nullRef = null; nullRef.intern(); } catch (NullPointerException ex) { System.out.println("null.intern() throws NPE as expected: " + ex.getClass().getSimpleName()); } // Defensive check if you accept user input. String maybeNull = null; String safe = internOrNull(maybeNull); System.out.println("safe value is null : " + (safe == null)); } private static String internOrNull(String value) { // This helper shows the right pattern when null is possible. return value == null ? null : value.intern(); } }