The Runtime Theory
mediumApplicationDSA#strings#interning#memory

Explain string interning and when == compares values

Tests whether you know interning as a memory dedup optimization, when the runtime actually interns, and the language-specific trap: reference equality vs value equality.

The Runtime Theory Team2 min readasked at google · meta · microsoft · amazon

This question tests two separate things and a strong answer separates them: the memory optimization (interning) and the equality semantics trap (==). The interviewer wants to know you understand both the mechanism and the footgun.

The mechanism. Interning means storing a canonical copy of a string in a runtime-wide table, so that equal strings share one object. Two interned strings that are equal are also identical: the same address. Interning is a memory optimization — one copy instead of N — and it turns equality checks into pointer comparisons. The compiler and runtime intern string literals: the same literal in source compiles to a reference to one table entry, which is why Java's "hello" == "hello" is true. What is not interned: runtime-constructed strings. "hello" + " world" in Java may or may not compile to a constant; a variable concatenation, new String("hello"), or reading from I/O always produces a fresh object.

The footgun: == on strings compares references, not content, in Java, C#, Python (identity vs equality aside — Python's is), and most languages where strings are objects. The classic failure:

java
String a = "hi";            // interned literal
String b = new String("hi"); // fresh heap object
a == b                      // false — different addresses
a.equals(b)                 // true — same content

In C++, std::string is a value type so == compares contents — but const char* comparison is pointer equality, same trap with different syntax. In C#, == is overloaded for string to compare content, while ReferenceEquals exposes the identity — so the trap is language-dependent, which is exactly what the interviewer is probing.

Edge cases worth naming:

  • The intern pool is a table with a lock; the JVM's String.intern() has a fixed table size and historically caused contention and memory leaks — manual interning is a measured decision, not a default.
  • Dedup at GC time: some runtimes (Java G1 with -XX:+UseStringDeduplication) deduplicate strings during collection as a cheaper alternative to eager interning.
  • Substrings share buffers in some implementations: s.substring(0, 3) kept a reference to the whole original array, so a small substring could pin a large string in memory — a real production leak pattern.
  • Concat vs literals: "a" + "b" where both are constants is folded at compile time and interned; s + "b" allocates a new string every iteration — the string-concat trace shows the heap growth.

A strong closing: "Interning buys one copy and pointer equality, but it only happens when the runtime says so — literals yes, runtime strings no — so never rely on == for content."

This answer walks

Follow-ups they'll push on

  1. 01How is an interned string different from a normal string in memory?
  2. 02Why do compilers and runtimes intern string literals but not concatenation results?
  3. 03How does String.equals differ from == in Java, and why?
  4. 04When is manual interning worth it in a real application?

More interviews in this topic

One dispatch a week

The trace behind each question, the tradeoff that explains it, and one technical dispatch per week — no noise.

One technical dispatch per week. No noise.