jdk 9起string默认启用compact strings,通过byte[]+coder字段自动选择latin-1(1字节/字符)或utf-16(2字节/字符)编码,纯ascii字符串内存减半,实测堆内存节省25%~30%。
![java中 string 在 jdk 9 紧凑字符串 compact strings 怎么用 byte[] 优化内存](https://img.php.cn/upload/article/001/242/473/178433649762023.jpeg?x-oss-process=image/resize,p_40)
Java 中 String 在 JDK 9 起默认启用 Compact Strings,它不是“怎么用”的 API 功能,而是 JVM 内部自动生效的内存优化机制——你不用写额外代码,只要运行在 JDK 9+,字符串就会根据内容自动选择更省空间的存储方式。
byte[] 是怎么替代 char[] 省内存的
JDK 8 及之前,String 固定用 char[] 存储,每个 char 占 2 字节(UTF-16),哪怕只存 "abc" 这种纯 ASCII 字符,也得占 6 字节数据空间。
JDK 9 改为 byte[] + coder 结构:
- coder == 0(LATIN1):字符串所有字符码点 ≤ 255(即 ASCII 或 Latin-1 字符),
byte[]每个元素存 1 字节,如 "hello" →byte[5],仅占 5 字节数据 + 1 字节 coder 字段 - coder == 1(UTF16):含中文、emoji 等宽字符,则
byte[]按 UTF-16 编码打包,每字符占 2 字节,如 "你好" →byte[4]
纯 Latin-1 字符串内存占用直降约 50%,实测堆中字符串总内存平均减少 25%~30%。
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
什么情况下真正启用 Latin-1 单字节存储
关键不是“看起来像英文”,而是 JVM 构造时一次性判定所有字符是否都落在 Latin-1 范围(U+0000–U+00FF):
- 字面量如
"user_id"、"HTTP/1.1"、JSON key("name")通常走 LATIN1 - 含任意一个非 Latin-1 字符(如 '你'、'α'、'?')→ 整个字符串升格为 UTF16,统一按 2 字节/字符存
- 拼接操作:
"a" + "b" + "你好"→ 结果必为 UTF16,哪怕前两段是 ASCII
怎么确认某个 String 是否用了 Latin-1 存储
不能靠 getBytes().length 或肉眼判断,必须查 coder 字段:
- 反射读取:
String.class.getDeclaredField("coder").get(str),返回 0 表示 Latin-1,1 表示 UTF16 - 调试时在 IDE 中展开 String 实例,直接观察
coder和value字段值 - 注意陷阱:
new String("a".getBytes(), ISO_8859_1)不一定保持 Latin-1,因为构造逻辑会先 decode 再 encode 判定,可能升格
哪些操作容易意外失去 Latin-1 优势
看似安全的操作,可能触发保守升格:
- 用
new String(char[])构造——JVM 无法静态分析内容,直接设为 UTF16 - 用
new String(byte[], "UTF-8")——指定 Charset 会绕过 Latin-1 快路径 -
StringBuilder.append("abc").append("你好").toString()→ 结果必为 UTF16
这不是 bug,而是为了语义一致性:字符串内容一旦可能含宽字符,就统一用 UTF-16 编码,避免混合编码带来的复杂性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










