JEP 254:紧凑字符串
概述
采用更节省空间的字符串内部表示形式。
目标
在大多数场景中保持性能并为所有相关的 Java 和本地接口保留完全兼容性的同时,提高 String
类和相关类的空间效率。
非目标
在字符串的内部表示中使用替代编码(如 UTF-8)并不是目标。后续的 JEP 可能会探讨这种方法。
动机
当前 String
类的实现使用 char
数组存储字符,每个字符占用两个字节(十六位)。从众多不同应用程序中收集的数据表明,字符串是堆使用的主要组成部分,而且大多数 String
对象仅包含 Latin-1 字符。这些字符只需要一个字节的存储空间,因此这些 String
对象的内部 char
数组有一半的空间未被使用。
描述
我们建议将 String
类的内部表示形式从 UTF-16 的 char
数组更改为 byte
数组加上一个编码标志字段。新的 String
类会根据字符串的内容,将字符存储为 ISO-8859-1/Latin-1(每个字符一个字节)或 UTF-16(每个字符两个字节)的编码格式。编码标志将指示使用的是哪种编码。
诸如 AbstractStringBuilder
、StringBuilder
和 StringBuffer
等与字符串相关的类将被更新为使用相同的表示形式,HotSpot 虚拟机的内置字符串操作亦是如此。
这完全是一个实现上的变更,现有公共接口没有任何变化。目前没有计划添加任何新的公共 API 或其他接口。
到目前为止完成的原型设计工作证实了内存占用的预期减少、GC 活动的大幅减少,以及在某些极端情况下出现的轻微性能下降。
更多细节请参见:
替代方案
我们在 JDK 6 的更新版本中尝试了一个“压缩字符串”功能,该功能通过一个 -XX
标志启用。启用后,String.value
会被更改为一个 Object
引用,可能指向一个 byte
数组(对于仅包含 7 位 US-ASCII 字符的字符串),或者是一个 char
数组。这个实现并未开源,因此难以维护,并且很难与主分支的 JDK 源代码保持同步。此功能后来已被移除。
测试
对平台如此基础的部分进行更改,全面的兼容性和回归测试将是至关重要的。
我们还需要确认已经实现了该项目的性能目标。必须对内存节省情况进行分析。应该使用广泛的工作负载进行性能测试,范围从专注的微基准测试到大规模服务器工作负载不等。
我们将鼓励整个 Java 社区通过这项变更进行早期测试,以便找出任何剩余的问题。
风险与假设
优化字符存储以节省内存可能会在运行时性能方面带来权衡。我们预计这将通过减少 GC(垃圾回收)活动来抵消,并且我们将能够保持典型服务器基准测试的吞吐量。如果无法做到这一点,我们将研究优化措施,以便在内存节省和运行时性能之间取得可接受的平衡。
其他近期的项目已经减少了字符串所使用的堆空间,特别是 JEP 192:G1 中的字符串去重。即使在消除重复之后,如果采用更高效的编码方式,剩余的字符串数据仍可占用更少的空间。我们假设该项目仍将带来与所需投入相符的收益。