和String说的那些悄悄话其一

927 阅读3分钟

小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动

本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金

首先,要简单介绍下String字符串

      在日常工作中使用的极为高频的数据结构。用来表示一切需要使用字符描述的对象。以身份证上的信息为例,姓名、性别、民族、地址、身份证号都是使用字符串来表示。拓展下,围绕着人们的现实生活中,一切对象的大部分属性都会用到字符串这一国民级的数据结构。

然后,谈下字符串对象的创建与存储

我们知道,创建个String对象可以直接赋值个字符串,也可以使用new声明。如下图所示:

这里要说下字符串常量池的概念,字符串常量池是为了减少JVM创建字符串的数量和提高数据的查询性能而维护的数据(StringTable)。在不同的JDK版本中,字符串常量池在JVM中的存储位置不一。下表是其具体存储位置信息:

那么问题来了。

第一,1.7之后为什么要调整字符串常量池的位置呢?

原因是1.7之前,永久代默认空间小且GC频率低,当大量的字符串无法回收时,容易进行FullGC产生STOP THE WORLD或着造成OOM:PermGen Space。

而在1.7之后,字符串常量池存储在堆中,堆中有大量的内存用于存储字符串,而且堆中分年轻代和老年代,GC频率高。

第二,1.9之后存储为什么改为byte[] 存储?

来自许多应用程序的统计,String是堆使用的主要组成部分,而大多数字符串对象只包含拉丁字符,而拉丁字符只需要一个字节的存储空间。因此对于部分场景,使用char[]存在浪费空间的现状。优化为byte[]之后,进而引入了编码标志位coder,用来表示使用的是utf-16编码(两个字节表示字符),还是Latin-1编码(单个字节表示字符)。这样,既节省了内存空间,也支持多样化数据的存储。

接着,说下字符串常量池的底层

String Pool使用的是固定大小的HashTable结构,同样在不同的JDK版本中其默认大小是不同的,可通过:-XX:StringTablesize设置其长度,具体区分如下图所示:

再者,探讨下字符串拼接的效率。

使用"+"号会默认(JDK1.5之后)生成StringBuilder对象并走append()方法(JDK1.5之前是StringBuffer)。以下图demo为例,

执行javac StringSeries.java, 而后javap StringSeries得到执行流程如下:

对应新建StringBuilder对象和执行apped()方法入口为下面两个图所示:

小总结

综上,当有大量字符串拼接时,建议使用StringBuilder声明对象并append,而不是使用String进行+操作,后者会频繁创建对象增加内存开销。下期,我们会专注String所提供的其他公共方法进行深入剖析。