当前位置: 首页 > 科技观察

JDK9为何要将String的底层实现由char[]改成了byte[]-

时间:2023-03-13 17:13:38 科技观察

为什么JDK9把String的底层实现从char[]改成了byte[]?]来存储字符串内容,为什么要这样做呢?说白了,从char[]到byte[],主要是为了节省字符串占用的内存。内存占用减少带来的另一个好处是GC次数也会减少。1、为什么要优化String来节省内存空间我们可以使用jmap-histo:livepid|head-n10命令查看堆中对象实例的统计信息,查看ClassLoader信息,finalizer队列。下面是我运行的ProgrammingMeow项目实例(基于Java8)的结果。其中String对象有17638个,占用内存423312字节,排名第三。由于Java8中String的内部实现仍然是char[],我们可以看到内存使用率排在首位的是char数组。char[]对象一共有17673个,占用内存1621352字节,排名第一。也就是说,对String进行优化以节省内存空间是非常有必要的。如果要优化一个没有String那么频繁使用的类库,那就很鸡肋了。后台管理系统+基于SpringBoot+MyBatisPlus+Vue&Element的用户小程序,支持RBAC动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。项目地址:https://github.com/YunaiV/ruoyi-vue-pro二、为什么byte[]可以节省内存空间?众所周知,char类型数据在JVM中占用两个字节,使用UTF-8编码,取值范围在'\u0000'(0)到'\uffff'(65,535)(含)之间。也就是说,用char[]表示String,即使String中的字符只能用一个字节表示,也会占用两个字节。在实际开发中,单字节字符出现的频率还是高于双字节字符的。当然,仅仅将char[]优化为byte[]是不够的,还要配合Latin-1编码方式,用单个字节表示字符,比UTF-8编码更省事。很大的空间。换句话说,对于:Stringname="jack";像这样,使用Latin-1编码,4个字节就够了。但是对于:Stringname="Xiaoer";这种木有办法只能用UTF16编码。在JDK9的String源码中,为了区分编码方式,增加了coder字段来区分。/***用于对*{@codevalue}中的字节进行编码的编码标识符。此实现中支持的值是**LATIN1*UTF16**@implNote该字段受VM信任,如果String实例为常量,则*常量折叠。构造后覆盖这个*字段会出问题。*/私有最终字节编码器;Java会根据字符串的内容自动设置相应的编码,可以是Latin-1,也可以是UTF16。也就是说从char[]到byte[],中文是两个字节,纯英文是一个字节。在此之前,中文是两个字节,英文也是两个字节。基于微服务的思想,搭建一个B2C电商场景下的项目。核心技术栈为SpringBoot+Dubbo。未来将重组SpringCloudAlibaba。项目地址:https://github.com/YunaiV/onemall三、为什么使用UTF-16而不是UTF-8?在UTF-8中,字符0-127用1个字节表示,使用与ASCII相同的编码。只有编号为128及以上的字符才用2、3或4个字节表示。如果只有一个字节,那么最高位为0;如果有多个字节,则第一个字节从最高位开始,连续几个位的值为1,用几个字节编码,其余字节从10开始。具体表达形式为:0xxxxxxx:一个字节;110xxxxx10xxxxxx:双字节编码形式(两个1开头);-1110xxxx10xxxxxx10xxxxxx:三字节编码形式(三个1开头);11110xxx10xxxxxx10xxxxxx10xxxxxx:四字节编码形式(前四个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就是获取chars个数,subString是获取number到chars个数组成的子串,偶数length返回chars个数。所以UTF-16可以看作是Java世界中的一种定长编码。