Under The Hood
backend java JDK 9+ (compact strings), JDK 8u20+ (deduplication)

Compact Strings (byte[] + coder flag) & String Deduplication

Last updated
Prerequisites:
Java String basics
Understanding of JVM heap memory
  • java
  • compact-strings
  • string
  • deduplication
  • memory
  • gc

Read at your depth

The practical view

Before JDK 9, String stored char[] (2 bytes per char, UTF-16 always). Compact Strings (JDK 9+) store byte[] plus a coder flag (LATIN1 or UTF16): if all characters fit in Latin-1, the string uses 1 byte per char — halving memory for most strings. This is automatic; String.length() still returns the character count. String Deduplication (-XX:+UseStringDeduplication, G1) is a GC feature that identifies duplicate String objects (same content) and makes them share the same backing char[]/byte[], saving heap when many strings repeat (e.g. JSON keys, log fields).

The same idea in other frameworks

react equivalent
N/A — JVM runtime feature.

Legacy vs modern

char[]-based Strings vs compact byte[]+coder Strings

The old layout costs 2 bytes per char always; compact strings use 1 byte per char for Latin-1 content, halving memory for typical data.

before → after
Pre-JDK9 char[] String
// ❌ OLD: String was backed by char[] — every char = 2 bytes, always
Compact byte[]+coder String
String s = "hello"; // ✅ NEW: byte[] with LATIN1 coder → 5 bytes

Interview gotchas

Context

This tests the layout knowledge — interviewers want the byte[]+coder design and how the JVM keeps the public API stable while optimizing internals.

The mechanical answer

Compact strings store the characters in a byte[] (not char[]) plus a byte coder flag: LATIN1 (0) when every char fits in one byte, UTF16 (1) otherwise. The length() is computed from the byte[] length shifted by the coder (for LATIN1, /1; for UTF16, /2), so the public API is unchanged. Operations that depend on indexing — charAt, substring, indexOf, equals, hashCode, compareTo — consult the coder and use optimized Latin-1 paths (the C2 compiler emits intrinsics keyed on the coder). When a string transitions to UTF16 (a non-Latin char is appended), the array is expanded to 2 bytes/char. This halves memory for typical ASCII-heavy data and improves cache locality, with negligible complexity cost because the coder check is a single branch.

Trap

The naive answer is 'compact strings always use one byte per char'. Only Latin-1-compatible strings do; non-Latin content flips to UTF16. Another trap: assuming charAt is now O(1) byte indexing — it must decode based on the coder, but remains O(1); code that assumed a char[] layout or used reflection into the value field breaks. Candidates who describe the coder-aware dispatch show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.