JDK 9 将 String 底层存储从 char[] 改为 byte[],核心目的是节省内存,通过紧凑字符串机制自动选择 Latin-1(1 字节/字符)或 UTF-16(2 字节/字符)编码,平均内存节省 25%~30%。

JDK 9 将 String 的底层存储从 char[] 改为 byte[],核心目的是**节省内存**,尤其在大量字符串只含 Latin-1 字符(如 ASCII)的常见场景下效果显著。
字符编码与存储冗余问题
Java 中 char 是 16 位无符号整数(0–65535),天然按 UTF-16 编码设计。但现实中,绝大多数字符串(如变量名、JSON 键、HTTP 头、日志消息等)仅使用 ASCII 或 Latin-1 范围(0–255)的字符。用 2 字节 char 存 1 字节就能表示的字符,造成 50% 的内存浪费。
JDK 9 引入“紧凑字符串”(Compact Strings)机制:内部用 byte[] 存储,并搭配一个 coder 字段(byte 类型)标识编码格式:
-
coder == 0→ Latin-1 编码(每个字符占 1 字节) -
coder == 1→ UTF-16 编码(每个字符占 2 字节,兼容原有行为)
运行时自动选择编码,对用户透明
这个切换完全由 JVM 在字符串创建或构造时自动判断完成,无需开发者干预。例如:
-
new String("hello")→ 全是 ASCII,coder = 0,底层byte[5] -
new String("你好")→ 含中文,需 UTF-16 表示,coder = 1,底层byte[4](两个 char × 2 字节) -
new String("hello世界")→ 混合内容,升格为 UTF-16,整个字符串统一用 2 字节/字符存储
这种“按需升格”策略保证了语义一致性:所有 String API 行为不变,length() 仍返回字符数(不是字节数),charAt(i) 逻辑也保持正确。
性能与兼容性权衡
改用 byte[] 带来内存下降,但也引入少量运行时开销:
- 每次访问字符(如
charAt、substring)需先查coder,再按不同逻辑解码 - Latin-1 字符串转成
char[](如调用toCharArray())需临时解码复制
但实测表明:内存收益远大于 CPU 开销,尤其在堆中字符串占比高、且以英文为主的应用中(如微服务、Web 容器),GC 压力明显降低。Java 官方测试显示平均内存节省约 25%~30%。
对开发者的影响很小,但需注意隐含行为
绝大多数代码不受影响。唯一需留意的是:
- 反射直接读取
String.value(即底层数组)会得到byte[],不再是char[],旧版反射工具可能出错 - 序列化、JNI 或某些深度优化代码若假设
value必为char[],需要适配coder字段 -
String不再是“不可变数组 + 不可变长度”,而是“不可变字节数组 + 不可变编码标识”,逻辑不可变性未变,但实现更精细
不复杂但容易忽略。

















