Before Java 9, every String stored its characters in a char[], using two bytes per character. Java 9 introduced Compact Strings (JEP 254): a String now stores a byte[] plus an encoding flag (coder). If all characters fit in Latin-1, each takes one byte; otherwise the String uses UTF-16 with two bytes per character. Most strings in typical applications are Latin-1, so this roughly halves their memory.
Key Points: • Internally: private final byte[] value; private final byte coder; (LATIN1 = 0, UTF16 = 1). • It's enabled by default and completely transparent: the String API didn't change. • Strings containing characters outside Latin-1 (Hindi, Chinese, emoji, many symbols) still use two bytes per character. • Less memory also means less garbage collection and better CPU cache use. • It can be turned off with -XX:-CompactStrings, which is rarely useful.
Code Example:
import java.nio.charset.StandardCharsets;
public class CompactStringsDemo {
static String storage(String s) {
boolean latin1 = s.chars().allMatch(c -> c <= 0xFF);
int bytes = latin1 ? s.length() : s.length() * 2;
return "\"" + s + "\" -> " + (latin1 ? "LATIN1" : "UTF16") + ", about " + bytes + " bytes of characters";
}
public static void main(String[] args) {
System.out.println(storage("JavaInterviewHub"));
System.out.println(storage("café"));
System.out.println(storage("नमस्ते"));
System.out.println(storage("Price: ₹499"));
// Encoded size on disk or the network is a different thing (UTF-8 here)
System.out.println("UTF-8 bytes of नमस्ते: " + "नमस्ते".getBytes(StandardCharsets.UTF_8).length);
}
}Output:
"JavaInterviewHub" -> LATIN1, about 16 bytes of characters "café" -> LATIN1, about 4 bytes of characters "नमस्ते" -> UTF16, about 12 bytes of characters "Price: ₹499" -> UTF16, about 22 bytes of characters UTF-8 bytes of नमस्ते: 18
Good to Know: The rupee sign ₹ is outside Latin-1, so "Price: ₹499" is stored entirely as UTF-16: a single non-Latin-1 character switches the whole String to two bytes per character. The example works out the storage from the characters, because the internal fields can't be read without special JVM options.
Interview Tip: A concise interview answer is:
"Since Java 9, a String stores its characters in a byte array with an encoding flag instead of a char array. If every character fits in Latin-1 it uses one byte per character, otherwise UTF-16 with two bytes. The API didn't change, but typical applications save a lot of heap and garbage-collection work, because most strings are plain English text."
Previous question: How does a switch statement on Strings work internally?
Next question: Why could substring() cause a memory leak in older Java versions, and is it still a problem?