自 Java 9 起,JDK 对 String 的内部表示方式进行了重要演进。这一变化旨在降低内存占用并提升运行时性能。尽管对外 API 基本保持稳定,但底层结构已经与 Java 8 有显著差异。

本文从内部数据结构、内存模型、字符数组存储策略以及相关类(如 StringBuilderStringBufferAbstractStringBuilder)的协同变化等方面,对两代实现进行系统阐述。


一、内部存储结构的核心变更

1、Java 8:基于 char[]

在 Java 8 中,String 底层使用 UTF-16 表示,每个字符占用 2 字节:

private final char[] value;

含义:

  • 无论字符是否可以使用单字节表示,均以 2 字节存储。
  • ASCII 字符(即 ISO-8859-1 范围)会浪费一半空间。
  • 相关类如 StringBuilderStringBufferAbstractStringBuilder 同样使用 char[]

2、Java 9+:基于字节数组 + 压缩字符串优化(Compact Strings)

自 Java 9 起,String 改为:

private final byte[] value;
private final byte coder; // 0 = LATIN1, 1 = UTF16

关键点:

  1. 采用可变编码方案(LATIN1 + UTF-16)

    • 当所有字符均为 LATIN1 可表示(单字节),使用 1 字节编码。
    • 否则使用 2 字节编码(UTF-16)。
  2. 显著降低 ASCII 主导场景的内存占用

    • 大量 Web 文本、JSON、日志数据可从 2 bytes/char 降至 1 byte/char。
  3. 整体性能提升

    • 更小的数据结构提高 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 在现代高流量应用中获得更高的效率。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐