Java 9 新特性—String 底层存储结构变更

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

在 Java 9 之前,String 的底层存储结构都是 char[]

public final class String
     implements java.io.Serializable, Comparable<String>, CharSequence {

  //The value is used for character storage.
  private final char value[];

}

每个 char 都以 2 个字节存储在内存中。然而 Oracle 的 JDK 开发人员调研了成千上万个应用程序的 heap dump 信息,他们注意到大多数字符串都是以 Latin-1 字符编码表示的,它只需要一个字节存储就够了,两个字节完全是浪费,这比 char 数据类型存储少 50%(1 个字节)。

所以,在 Java 9 中将 String 的底层存储结构调整为 byte[]:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc {

    @Stable
    private final byte[] value;

    private final byte coder;
}

Java 9 这样调整的目的是减小字符串的内存占用,这样带来了两个好处:

  1. 节省内存:对于包含大量ASCII字符的字符串,内存占用大幅减少,因为每个字符只占用一个字节而不是两个字节。
  2. 提高性能:由于字符串的存储结构与编码方式更加紧凑,字符串操作的性能也有所提高。

需要注意的是,这仅仅只是底层数据结构的变化,对于我们上层调用者完全是透明的,不会有任何影响,String 的方法以前怎么使用,现在还是怎么使用,例如:

public class StringTest {
    public static void main(String[] args) {
        String skString1 = "skjava";
        String skString2 = "死磕Java";
        System.out.println(skString1.charAt(0));
        System.out.println(skString2.charAt(0));
    }
}
// 结果......
s
死

charAt() 源码如下:

    public char charAt(int index) {
        if (isLatin1()) {
            return StringLatin1.charAt(value, index);
        } else {
            return StringUTF16.charAt(value, index); 
        }
    }

isLatin1() 用于判断编码格式是否为 Latin-1 字符编码,如果是则调用 StringLatin1.charAt(),否则调用 StringUTF16.charAt()。这里为什么要判断字符编码呢?Latin-1 字符编码也称 ISO 8859-1,它包括了拉丁字母(包括西欧、北欧和南欧语言的字母)以及一些常见的符号和特殊字符,但是它并不支持其他非拉丁字母的语言,例如希腊语、俄语或中文,对于这些我们只能使用其他字符编码了。

在 Java 9 中,String 支持的字符编码格式有两种:

  1. Latin-1Latin-1 编码用于存储只包含拉丁字符的字符串。它采用了一字节编码,每个字符占用一个字节(8位)。
  2. UTF-16UTF-16 编码用于存储包含非拉丁字符的字符串,以及当字符串包含不适合 Latin-1 编码的字符时。

在 Java 9 中,String 多了一个成员变量 coder,它代表编码的格式,0 表示 Latin-1 ,1 表示 UTF-16,我们在看 skString1 和 skString2

从这张图可以清晰地看到 “skjava” 的字符编码是 Latin-1,而 “死磕Java” 的字符编码则是 UTF-16。不同的字符编码选择不同的方法来获取。其实你看下 String 里面的方法都是这种模式。

所以,Java 9 中的 String 使用 Latin-1 和 UTF-16 两种字符编码方式,根据字符串的内容来选择合适的编码格式,以便在内部存储时提高效率。

但是,有小伙伴就喜欢硬扛,我就不喜欢 Latin-1,可以完全用UTF-16 么 ?可以。Java 满足你的一切不合理的要求。

-XX:-CompactStrings:禁用精简字符串特性

  1. 如果启用 Compact Strings(默认情况),JVM 会根据字符串的内容来选择 Latin-1 还是 UTF-16,以在内存中有效地存储字符串,减小内存占用。
  2. 如果禁用 Compact Strings(使用 -XX:-CompactStrings),JVM 将始终使用 UTF-16 编码来存储字符串。

一般来说,我们是不需要显式设置 -XX:-CompactStrings,开启 Compact Strings 能够帮组我们节约内存和提高性能。

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值