当前位置: 首页 > article >正文

JDK17新特性之--新的Compact Strings(JEP 254)

前言

JDK9将String底层的数据结构从private final char value[];改成了private final byte[] value;
JEP 254: Compact Strings(紧凑字符串),这要修改的目的就是为了节省空间1。我们先看一下JDK9和JDK8中String源码的变化。
JDK9中String源码:

public final class String 
implements java.io.Serializable, Comparable<String>, CharSequence,  
Constable, ConstantDesc {  
    @Stable  
    private final byte[] value;  

    * LATIN1  
    * UTF16  
    private final byte coder;
    
    @Native static final byte LATIN1 = 0;  
    @Native static final byte UTF16 = 1;

JDK8中String源码:

public final class String  
    implements java.io.Serializable, Comparable<String>, CharSequence {  
    private final char value[];  
    private int hash; 
}

对比源码可以看出,在JDK9中使用了byte[]代替了char[]同时还增加了一个 coder标志位表示使用的是LATIN1还是UTF16编码。

节省了多大空间

既然这个升级是为了节省内存空间,那么我们就先来测试一下看看节省了多少空间。我们创建一个简单的Demo,创建一个List然后向里面添加1-20000000个String,再对比一下JDK9和JDK8占用内存空间大小。

public static void main(String[] args) throws InterruptedException {  
        List<String> name= IntStream.range(0,20000000).mapToObj(String::valueOf).collect(Collectors.toList());;  
        System.out.printf("add complete "+name.size());  
        Thread.sleep(100000000L);
}

JDK8内存使用情况

JDK8内存使用情况

JDK9内存使用情况

JDK9内存使用情况

从图中可以看出同样是1-20000000字符串JDK8中char[]占用了742MB,JDK9中byte[]占用了559MB,节省了32.7%内存空间。

没有Jprofile 也可以使用jps+jmap查看内存占用情况,以下为Windows操作

  1. .\jps.exe 获取出PID
PS C:\Program Files\Java\jdk1.8.0_191\bin> .\jps.exe
9328 RemoteMavenServer36
9744 Launcher
10548
2196 Jps
7224 RemoteMavenServer36
8120 Main
  1. 通过Jmap查看内存中对象统计
JDK8:
PS C:\Program Files\Java\jdk1.8.0_191\bin> .\jmap.exe -histo:live 8120

 num     #instances         #bytes  class name
----------------------------------------------
   1:      20004394      640375664  [C
   2:      20004267      480102408  java.lang.String
   3:           639       83113224  [Ljava.lang.Object;
   4:           685          77712  java.lang.Class
   5:           791          31640  java.util.TreeMap$Entry
   6:            26          25752  [B
   7:           657          21024  java.util.HashMap$Node
   8:           315          13488  [Ljava.lang.String;
   9:           126           8272  [I
  10:           123           7872  java.net.URL
  
JDK9:

PS C:\Program Files\Java\jdk1.8.0_191\bin> .\jmap.exe -histo:live 4116
 num     #instances         #bytes  class name (module)
-------------------------------------------------------
   1:      20003758      480237256  [B (java.base@17.0.7)
   2:      20003674      480088176  java.lang.String (java.base@17.0.7)
   3:          1042       83141776  [Ljava.lang.Object; (java.base@17.0.7)
   4:           137         142376  [C (java.base@17.0.7)
   5:           818         100864  java.lang.Class (java.base@17.0.7)
   6:          1242          39744  java.util.HashMap$Node (java.base@17.0.7)
   7:          1084          34688  java.util.concurrent.ConcurrentHashMap$Node (java.base@17.0.7)
   8:           330          30240  [Ljava.util.HashMap$Node; (java.base@17.0.7)
   9:           336          16128  java.util.HashMap (java.base@17.0.7)
  10:            22          15392  [Ljava.util.concurrent.ConcurrentHashMap$Node; (java.base@17.0.7)

从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等都重写了。

    public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;
    }

   public int compareTo(String anotherString) {
        byte v1[] = value;
        byte v2[] = anotherString.value;
        byte coder = coder();
        if (coder == anotherString.coder()) {
            return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
                                   : StringUTF16.compareTo(v1, v2);
        }
        return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)
                               : StringUTF16.compareToLatin1(v1, v2);
     }

3.更快的字符串操作:

由于 Compact Strings 的内部表示方式采用了byte[]存储数据,可以更快地执行字符串操作,例如字符串比较、拼接等。

String生成测试Demo:

        Long startTime=System.currentTimeMillis();
        List<String> name= IntStream.range(0,20000000).mapToObj(String::valueOf).collect(Collectors.toList());;
        System.out.println("add  "+name.size()+" complete ,cast "+(System.currentTimeMillis()-startTime)+"ms"); 
       //JDK9:
       add  20000000 complete ,cast 1313ms
       //JDK8
       add  20000000 complete ,cast 13777ms

从生成2000万个String速度来看,JDK9只花了1.3S,而JDK8花了13S,确实快了不少。

String拼接Demo:

        Long startTime = System.currentTimeMillis();
        List<String> name = IntStream.range(0, 20000000).mapToObj(x -> String.valueOf(x) + x).collect(Collectors.toList());
        System.out.println("add  " + name.size() + " complete ,cast " + (System.currentTimeMillis() - startTime) + "ms");
  //JDK9
  add  20000000 complete ,cast 1832ms
  //JDK8
  add  20000000 complete ,cast 25147ms
        

从拼接2000万个String速度来看,JDK9只花了1.8S,而JDK8花了25S,确实也快了不少。

4.更高的兼容性:

Compact Strings 不会影响现有的 Java 代码,因此可以与现有的 Java 应用程序兼容,无需修改代码,这点是肯定的,如果要修改Java String操作的代码全球有多少人要抓狂,最后肯定都选择不升级了。


  1. JEP 254: Compact Strings (openjdk.org) ↩︎

  2. (11 封私信) Java中关于Char存储中文到底是2个字节还是3个还是4个? - 知乎 (zhihu.com) ↩︎

  3. ISO/IEC 8859-1 - Wikipedia ↩︎


http://www.kler.cn/a/17447.html

相关文章:

  • ⾃动化运维利器Ansible-基础
  • Springboot配置全局异常通用返回
  • 使用API有效率地管理Dynadot域名,编辑账户中whois联系人信息
  • 开发语言中,堆区和栈区的区别
  • LeetCode【0035】搜索插入位置
  • ubuntu-desktop-24.04上手指南(更新阿里源、安装ssh、安装chrome、设置固定IP、安装搜狗输入法)
  • 以两分钟了解Python之SQLAlchemy框架的使用
  • 腾讯云的cdn怎么配置|腾讯云CDN配置教程
  • ps磨皮插件专用智能磨皮插件 AI算法美颜 提高P图效率
  • 如何在Emacs中设置字体大小?- 探索Emacs的设置字体大小方法
  • 微信小程序——自定义组件
  • 关于数据倾斜
  • 【Linux】基础IO——库函数与系统调用的关系
  • 牛客刷SQL题Day5
  • Golang中sync.Pool详解及使用方法
  • ( “ 图 “ 之 拓扑排序 ) 210. 课程表 II ——【Leetcode每日一题】
  • 服务器中了勒索病毒,malox勒索病毒的加密方式及如何应对勒索病毒攻击
  • 【刷题笔记】二维数组地址计算+算法分析+进制转换
  • 计算机网络基础知识(二)—— 什么是Ip地址、Mac地址、网关、子网掩码、DNS
  • SpringBoot实现导出Excel功能
  • 汽车出租系统【纯控制台】(Java课设)
  • 互联网摸鱼日报(2023-05-02)
  • 计算机必读基础书籍
  • 【圈友app】为什么要使用MongoDB数据库?什么样的数据适合存储到MongoDB数据库?
  • 【C++学习】类和对象--多态
  • 牛客网HJ31 单词倒排