Java 8 与 Java 9 之后:String 实现的关键差异
·
文章目录
自 Java 9 起,JDK 对
String 的内部表示方式进行了重要演进。这一变化旨在降低内存占用并提升运行时性能。尽管对外 API 基本保持稳定,但底层结构已经与 Java 8 有显著差异。
本文从内部数据结构、内存模型、字符数组存储策略以及相关类(如 StringBuilder、StringBuffer、AbstractStringBuilder)的协同变化等方面,对两代实现进行系统阐述。
一、内部存储结构的核心变更
1、Java 8:基于 char[]
在 Java 8 中,String 底层使用 UTF-16 表示,每个字符占用 2 字节:
private final char[] value;
含义:
- 无论字符是否可以使用单字节表示,均以 2 字节存储。
- ASCII 字符(即 ISO-8859-1 范围)会浪费一半空间。
- 相关类如
StringBuilder、StringBuffer、AbstractStringBuilder同样使用char[]。
2、Java 9+:基于字节数组 + 压缩字符串优化(Compact Strings)
自 Java 9 起,String 改为:
private final byte[] value;
private final byte coder; // 0 = LATIN1, 1 = UTF16
关键点:
-
采用可变编码方案(LATIN1 + UTF-16)
- 当所有字符均为 LATIN1 可表示(单字节),使用 1 字节编码。
- 否则使用 2 字节编码(UTF-16)。
-
显著降低 ASCII 主导场景的内存占用
- 大量 Web 文本、JSON、日志数据可从 2 bytes/char 降至 1 byte/char。
-
整体性能提升
- 更小的数据结构提高 CPU cache 命中率。
- 字符操作(substring、比较等)的编译器内联能更高效利用 byte 数组。
二、String 相关类的配套调整
1. StringBuilder 和 StringBuffer
- Java 8:基于
char[] - Java 9+:改为基于
byte[],并配合 coder 标志位
设计目标是保持与 String 一致的字节与编码模型,以便减少转换成本。
2. AbstractStringBuilder
核心改动与 String 保持同步:
- 字段从
char[]改为byte[]。 - 新增
byte coder,确保 builder 可以根据字符串内容选择最优编码。
该调整直接带来构建字符串时的空间效率提升。
三、equals、hashCode、compareTo 的行为和性能变化
行为:保持完全向后兼容
- API 语义保持一致,不影响业务逻辑。
性能:在 ASCII 主要场景显著提升
- equals/compareTo 在 LATIN1 模式下仅需逐字节比较,效率比 16 bit char 快。
- hashCode 计算也受益于更紧凑的数据格式。
四、内存占用差异对比示例
假设字符串 "Hello World"(均为 ASCII 字符):
Java 8:
- char[] = 11 chars × 2 bytes = 22 bytes(不计对象头)
Java 9+:
- byte[] = 11 chars × 1 byte = 11 bytes
- coder = 1 byte
结果:减少了近 50% 的存储开销。
对于大量文本数据的系统(如高流量服务端应用),这一变化能明显降低堆内存消耗。
五、总结
| 特性 | Java 8 | Java 9+ |
|---|---|---|
| 底层存储 | char[] |
byte[] + coder |
| 编码方式 | UTF-16 固定 2 字节 | LATIN1 或 UTF-16 动态选择 |
| 内存占用 | 较高 | 对 ASCII 字符降低 50% |
| substring 实现 | 拷贝 char[] | 拷贝 byte[] |
| 对外 API | 稳定 | 稳定 |
| 性能 | 较为稳定 | 更优的空间与速度表现 |
这一优化是 Java 9 模块化体系之外最重要的运行时特性之一,使 String 在现代高流量应用中获得更高的效率。
更多推荐
所有评论(0)