小知识,大挑战!本文正在参与“ 程序员必备小知识 ”创作活动
本文同时参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
首先,要简单介绍下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所提供的其他公共方法进行深入剖析。