String Table
概述
- String是final类
- String实现了Serializable、Comparable < String >、CharSequence接口
- 在jdk9以前使用char数组储存字符串数据,jdk8以后使用byte数组
@Stable
private final byte[] value;
- String 对象是不可变的
- 通过字面量的方式(String str = "str")给一个字符串赋值,字符串值声明在字符串常量池中
- 字符串常量池不会储存相同内容的字符串
- String的String Pool是一个固定大小的HashTable,在String Pool中存放的String过多会造成Hash冲突严重,从而导致链表很长,继而导致调用String.intern时性能会大幅下降
- 可以使用参数 -XX:StringTableSize 设置StringTable的长度,jdk8中StringTable最小值为1009,jdk11中StringTable最小值为128,最大值为16777216
String的内存分配
- 直接使用双引号声明的String会存放在字符串常量池中
- 使用String类的intern()方法
- jdk7以前字符串常量池存放在永久代中
- jdk7中字符串常量池存放在堆中
字符串拼接
- 常量与常量的拼接结果在常量池,因为编译期优化
public void hello() {
String str1 = "a" + "b";
String str2 = "ab";
System.out.println(str1 == str2);
}
2. 常量池中不会存放相同内容的常量
3. 只要其中有一个是变量,结果就在堆中,使用了StringBuilder
public void hello() {
String str1 = "a" + "b";
String str2 = "a";
String str3 = str2 + "b";
}
4. 如果拼接的结果调用String类的intern()方法,将常量池中还没有的字符串对象放入池中,并返回对象地址
intern()
- 当调用 intern 方法时,如果池中已经包含String由equals(Object)方法确定的等于此对象的字符串,则返回池中的字符串。否则,将此String对象添加到池中并String返回对此对象的引用。
- 如果不是双引号声明的String对象,可以使用String类的intern()方法
- new String("abc")会创建几个对象?
两个
- newString("a") + new String("b")会创建几个对象?
对象1:new String("a")、对象2:常量池中的"a"、对象3:new String("b")、对象4:常量池中的"b"、对象5:new StringBuilder()、对象6:StringBuilder.toString()方法的new String()
- new String()返回的是堆空间的对象
- jdk1.6中调用intern方法,将这个字符串对象尝试放入字符串常量池
6.1 如果字符串常量池里有,返回这个对象在字符串常量池的地址
6.2 如果没有,把这个对象复制一份,放入到字符串常量池中,返回字符串常量池中的对象地址 - 从jdk1.7开始,调用intern方法,将这个字符串对象尝试放入字符串常量池
7.1 如果字符串常量池里有,返回这个对象在字符串常量池的地址
7.2 如果没有,把对象的引用地址复制一份并放入字符串常量池中,返回字符串常量池中的引用地址 - 对于存在着大量重复的字符串的程序,可以使用intern()节省空间
StringTable的垃圾回收
- 在G1垃圾收集器中实现自动持续对重复的String对象进行去重
- 当垃圾收集器工作的时候,会访问堆上存活的对象。对每一个访问的对象都会检查是否是候选的要去重的string对象
- 如果是,把这个对象的一个引用插入到队列中等待后续的处理。一个去重的线程在后台运行,处理这个队列。处理队列的一个元素意味着从队列删除这个元素,然后尝试去重它引用的string对象
- 使用一个hashtable来记录所有的被string对象使用的不重复的char数组。当去重的时候,会查这个hashtable,来看堆上是否已经存在一个一模一样的char数组
- 如果存在,String对象会被调整引用那个数组,释放对原来的数组的引用,最终会被垃圾收集器回收掉
- 如果查找失败,char数组会被插入到hashtable,这样以后的时候就可以共享这个数组
- -XX:+UseStringDeduplication:开启String去重,默认不开启(jdk11—)
- -Xlog:stringdedup*=debug:打印详细的去重统计信息(jdk11)
- -XX:StringDeduplicationAgeThreshold=?:到达这个年龄的String对象被认为是去重的候选对象