1 String类的底层演变
JDK8以及之前的版本
JDK9以及之后的版本
JDK8的字符串存储在char类型的数组里面,在java中,一个char类型占两个字节。但是很多时候,一个字符只需要一个字节就可存储,比如各种字母什么的,两个字节存储势必会浪费空间,JDK9的一个优化就在这,内存的优化,所以JDK9之后字符串改成byte类型数组进行存储。
private final byte coder;
在JDK9的String类中,新增了一个属性coder,它是一个编码格式的标识,使用LATIN1还是UTF16,这个是在String生成的时候自动确定的,如果字符串中都是能用LATIN1编码表示,那coder的值就是0,否则就是UTF16编码,coder的值就是1。
可以看到JDK9在这方面的优化,在较多情况下不包含那些奇奇怪怪的字符的时候,足以应付,而这个空间却小了1byte,实现了String空间的压缩。
2 String常量池的演变
2.1 StringTable变化
String 的 String Pool是一个固定大小的 Hashtable。
在jdk6中,StringTable的长度固定为1009。
如果放进 String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用 intern() 时性能会大幅下降。
从jdk7起,StringTable的长度默认值是60013。
使用-XX:StringTableSize可设置StringTable的长度。
在jdk8之前,对StringTableSize的设置没有最小限制。
jdk8开始,StringTable可设置的最小值是1009。
验证:
通过 jps 命令查看进程号
使用 jinfo -flag StringTableSize 进程号 查看StringTable大小
2.2 内存位置变化
Java6及以前,字符串常量池存放在永久代。
Java7开始,字符串常量池的位置调整到Java堆内。
所有的字符串都保存在堆(Heap)中,和其他普通对象一样,这样在进行调优应用时仅需要调整堆大小就可以了。
官网说明
https://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes
JDK6环境下测试:
/*
jdk6中,修改JVM内存大小:
-XX:PermSize=6m -XX:MaxPermSize=6m -Xms6m -Xmx6m
*/
public class StringTableTest {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
int i=0;
while (true){
set.add(String.valueOf(i++).intern());
}
}