前言
JDK9将String底层的数据结构从private final char value[];
改成了private final byte[] value;
,
JEP 254: Compact Strings(紧凑字符串),这要修改的目的就是为了节省空间1。我们先看一下JDK9和JDK8中String源码的变化。
JDK9中String源码:
JDK8中String源码:
对比源码可以看出,在JDK9中使用了byte[]代替了char[]同时还增加了一个 coder标志位表示使用的是LATIN1还是UTF16编码。
节省了多大空间
既然这个升级是为了节省内存空间,那么我们就先来测试一下看看节省了多少空间。我们创建一个简单的Demo,创建一个List然后向里面添加1-20000000个String,再对比一下JDK9和JDK8占用内存空间大小。
JDK8内存使用情况
JDK9内存使用情况
从图中可以看出同样是1-20000000字符串JDK8中char[]占用了742MB,JDK9中byte[]占用了559MB,节省了32.7%内存空间。
没有Jprofile 也可以使用jps+jmap查看内存占用情况,以下为Windows操作
- .\jps.exe 获取出PID
- 通过Jmap查看内存中对象统计
从Jmap输出内存对象统计来看 JDK8中[C就是 char[] ,实例数有2000万多点,占用内存610MB,JDK9 [B 就是byte[],实例数也是2000万多点,占用内存457MB,节省内存33.4%。值的注意的是如果不想使用字符串压缩可以使用 -XX:-CompactStrings
JVM参数来禁用此功能
是怎么节省空间的
JAVA中一个char到底占用多大空间?在JVM规范中char是使用一个16-bit无符号整型来表示一个Unicode code2,所以一个char可以理解占用两个字节空间,但是我们可以想一下常用的ascii码其实只要一个字节就能表示了。JDK9里对Latin-1(又称ISO-8859-1)字符使用一个byte存储,只占用了一个字节,如果不是Latin-1字符就使用UTF-16存储,占用两个字节,为此JDK9中增加了coder标志位,这样对于使用最频繁的abcd… 1234…等字母、数字来说就节省了50%空间,然而对于中文或者不在Latin-1字符来说还是和以前一样,并不能节省内存空间。那么Latin-1到底有哪些字符呢?Latin-1(也称为ISO 8859-1),是一种字符编码标准,用于表示拉丁字母语系的字符集。它包含了256个字符3。
- 26个基本拉丁字母,包括大小写字母 a-z,A-Z
- 数字0-9
- 逗号、句号、问号等常见标点符号
- 特殊符号,如@、#、$、%等
- 一些重音符号和其他符号,如é、ç、ñ等
具体可以看以下图片:
总结
1. 更少的内存占用:
采用 Compact Strings 后,只包含 ASCII 字符或者Latin-1的字符串在内存中的占用空间会减少一半,从而可以减少内存的使用量,提高程序的性能。
2.String内置方法都重写了
String内置的方法像equals、compareTo等都重写了。
3.更快的字符串操作:
由于 Compact Strings 的内部表示方式采用了byte[]存储数据,可以更快地执行字符串操作,例如字符串比较、拼接等。
String生成测试Demo:
从生成2000万个String速度来看,JDK9只花了1.3S,而JDK8花了13S,确实快了不少。
String拼接Demo:
从拼接2000万个String速度来看,JDK9只花了1.8S,而JDK8花了25S,确实也快了不少。
4.更高的兼容性:
Compact Strings 不会影响现有的 Java 代码,因此可以与现有的 Java 应用程序兼容,无需修改代码,这点是肯定的,如果要修改Java String操作的代码全球有多少人要抓狂,最后肯定都选择不升级了。
- JEP 254: Compact Strings (openjdk.org) ↩︎
- (11 封私信) Java中关于Char存储中文到底是2个字节还是3个还是4个? - 知乎 (zhihu.com) ↩︎
- ISO/IEC 8859-1 - Wikipedia ↩︎