欢迎关注方志朋的博客,回复”666“获面试宝典

如果你不是 Java8 的钉子户,你应该早就发现了:String 类的源码已经由 char[] 优化为了 byte[] 来存储字符串内容,为什么要这样做呢?

开门见山地说,从 char[] 到 byte[],最主要的目的是为了节省字符串占用的内存。内存占用减少带来的另外一个好处,就是 GC 次数也会减少。

一、为什么要优化 String 节省内存空间

我们使用 jmap -histo:live pid | head -n 10 命令就可以查看到堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。

以我正在运行着的编程喵喵项目实例(基于 Java 8)来说,结果是这样的。

图片

其中 String 对象有 17638 个,占用了 423312 个字节的内存,排在第三位。

由于 Java 8 的 String 内部实现仍然是 char[],所以我们可以看到内存占用排在第 1 位的就是 char 数组。

char[] 对象有 17673 个,占用了 1621352 个字节的内存,排在第一位。

那也就是说优化 String 节省内存空间是非常有必要的,如果是去优化一个使用频率没有 String 这么高的类库,就显得非常的鸡肋。

二、byte[] 为什么就能节省内存空间呢?

众所周知,char 类型的数据在 JVM 中是占用两个字节的,并且使用的是 UTF-8 编码,其值范围在 '\u0000'(0)和 '\uffff'(65,535)(包含)之间。

也就是说,使用 char[] 来表示 String 就导致了即使 String 中的字符只用一个字节就能表示,也得占用两个字节。

而实际开发中,单字节的字符使用频率仍然要高于双字节的。

当然了,仅仅将 char[] 优化为 byte[] 是不够的,还要配合 Latin-1 的编码方式,该编码方式是用单个字节来表示字符的,这样就比 UTF-8 编码节省了更多的空间。

换句话说,对于:

String name = "jack";

这样的,使用 Latin-1 编码,占用 4 个字节就够了。

但对于:

String name = "小二";

这种,木的办法,只能使用 UTF16 来编码。

针对 JDK 9 的 String 源码里,为了区别编码方式,追加了一个 coder 字段来区分。

/**  * The identifier of the encoding used to encode the bytes in  * {@code value}. The supported values in this implementation are  *  * LATIN1  * UTF16  *  * @implNote This field is trusted by the VM, and is a subject to  * constant folding if String instance is constant. Overwriting this  * field after construction will cause problems.  */
private final byte coder;

Java 会根据字符串的内容自动设置为相应的编码,要么 Latin-1 要么 UTF16。

也就是说,从 char[] 到 byte[]中文是两个字节,纯英文是一个字节,在此之前呢,中文是两个字节,英文也是两个字节

三、为什么用UTF-16而不用UTF-8呢?

在 UTF-8 中,0-127 号的字符用 1 个字节来表示,使用和 ASCII 相同的编码。只有 128 号及以上的字符才用 2 个、3 个或者 4 个字节来表示。

  • 如果只有一个字节,那么最高的比特位为 0;

  • 如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1,就使用几个字节编码,剩下的字节均以 10 开头。

具体的表现形式为:

  • 0xxxxxxx:一个字节;

  • 110xxxxx 10xxxxxx:两个字节编码形式(开始两个 1);- 1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式(开始三个 1);

  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式(开始四个 1)。

也就是说,UTF-8 是变长的,那对于 String 这种有随机访问方法的类来说,就很不方便。所谓的随机访问,就是charAt、subString这种方法,随便指定一个数字,String要能给出结果。如果字符串中的每个字符占用的内存是不定长的,那么进行随机访问的时候,就需要从头开始数每个字符的长度,才能找到你想要的字符。

那有小伙伴可能会问,UTF-16也是变长的呢?一个字符还可能占用 4 个字节呢?

的确,UTF-16 使用 2 个或者 4 个字节来存储字符。

  • 对于 Unicode 编号范围在 0 ~ FFFF 之间的字符,UTF-16 使用两个字节存储。

  • 对于 Unicode 编号范围在 10000 ~ 10FFFF 之间的字符,UTF-16 使用四个字节存储,具体来说就是:将字符编号的所有比特位分成两部分,较高的一些比特位用一个值介于 D800~DBFF 之间的双字节存储,较低的一些比特位(剩下的比特位)用一个值介于 DC00~DFFF 之间的双字节存储。

但是在 Java 中,一个字符(char)就是 2 个字节,占 4 个字节的字符,在 Java 里也是用两个 char 来存储的,而String的各种操作,都是以Java的字符(char)为单位的,charAt是取得第几个char,subString取的也是第几个到第几个char组成的子串,甚至length返回的都是char的个数。

所以UTF-16在Java的世界里,就可以视为一个定长的编码。

参考链接:https://www.zhihu.com/question/447224628

来源:沉默王二

热门内容:

  • 我妈今年 70 岁,受不了Windows蓝屏,用了 21 年的 Linux!YYDS!

  • 京东一面:Spring 为何需要三级缓存解决循环依赖,而不是二级缓存?我懵了。。

  • 牛啊,又一份牛逼笔记面世了

最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。
获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

JDK9为何要将String的底层实现由char[]改成了byte[]?相关推荐

  1. JDK9的新特性:String压缩和字符编码

    文章目录 简介 底层实现 总结 简介 String的底层存储是什么?相信大部分人都会说是数组.如果要是再问一句,那么是以什么数组来存储呢?相信不同的人有不同的答案. 在JDK9之前,String的底层 ...

  2. String的底层分析 (学习笔记)

    StringTable底层分析 String的基本特性 StringPool String的内存分配 字符串的拼接操作 拼接效率的对比 intern()的理解 new String("&qu ...

  3. char转成string_真没想到,一个小小的String居然还有这么多窍门?

    推荐学习 公司来了位阿里P8大神,看完他的手写"Kafka笔记",万分膜拜 牛掰!"基础-中级-高级"Java程序员面试集结,看完献出我的膝盖 真没想到,一个小 ...

  4. arm linux c++编译警告:ISO C++ forbids converting a string constant to ‘char*’(需要把const char*转换成char*)

    编译时遇到警告: /home/yg/rv/sdk/rv1.8/rv1126_rv1109/buildroot/output/rockchip_rv1126_rv1109/build/ky_ai_cam ...

  5. int char转换成string java,java中int,char,string三种类型的相互转换

    如何将字串 String 转换成整数 int? int i = Integer.valueOf(my_str).intValue(); int i=Integer.parseInt(str); 如何将 ...

  6. java如何将char转换成string_java中  如何将char类型转换成string 类型

    将char转换为String大致有6种方法.总结如下: 1.String s = String.valueOf('c'); //效率最高的方法 2.String s = String.valueOf( ...

  7. Go语言的string(底层结构+常用方法)

    字符串 Go语言中的字符串是通过UTF-8编码,字符串的值为双引号(")中的内容,可以在Go语言的源码中直接添加非ASCII码字符 . 字符串底层结构是一个起始地址和长度(字节个数) 字符串 ...

  8. Java 分割字符串的方法String.split()底层原理

    文章目录 1.举例说明 2.split源码分析 3.API原解 4.regex参数API原解 5.limit参数介绍 6.结果的验证 7.此方法的使用 1.举例说明 (例子来自于博主:https:// ...

  9. String的底层结构(使用频率较高的)(java.lang.invoke.MethodHandleImpl.MAX_ARITY)

    类 1 String被final修饰 说明String 无法被其他类继承 2 实现Serializable接口 说明String可以序列化 3 实现Comparable接口 String可以进行对应的 ...

最新文章

  1. FCN与U-Net语义分割算法
  2. Java中使用LUA脚本语言
  3. c++美发店管理系统设计_美发店如何打造会员管理系统?掌柜智囊—收银机必不可少...
  4. 利用ASP下载远程文件到服务器
  5. WPF学习:分页控件
  6. java web 部署_一步一步将java web项目部署到云服务器
  7. 算法与数据结构 第1章 当我们谈论算法的时候,我们在谈论什么?
  8. Linux表示什么、如何使用、在哪里使用、解释、简单操作
  9. JavaCV 制作字符画
  10. 爬虫日记(6):beautifulsoup的基本使用2
  11. 游苹果山赋——东南子(2010年旧文)
  12. php 图片生成视频,图片转化为视频的方法 如何将照片制作成为视频
  13. 【摘】UI设计中对比色颜色的选取
  14. Python学习笔记(1)——提取百度新闻标题、网址、日期和来源
  15. shit 环信 IM SDK IM SDK web
  16. ​Linux最强总结!
  17. 激光雷达鼻祖Velodyne谋求合并求生:高层动荡 亏损不止
  18. 加薪申请函--2018加油!!!
  19. java高校学生电器报修系统ssm高校后勤报修系统小程序源码和论文
  20. Ansys Zemax | 利用 Kogelnik 方法模拟体全息光栅的衍射效率

热门文章

  1. 关于计算机网络传输介质 下列叙述正确的是,《计算机基础》习题1-7
  2. 自动驾驶安全框架开发进展综述
  3. tensorflow 协同过滤_基于django和协同过滤/cnn的电影推荐系统
  4. 对支持向量机(SVM)的个人理解
  5. 5.matlab 中的sort
  6. Vijos1683 有根树的同构问题
  7. activiti5/6 系列之--BpmnModel使用
  8. 第十一周总结CoreIDRAW
  9. zookeeper图形工具——zkui
  10. angular4 note