Bootstrap

String字符串常量池对象和引用

String字符串常量池对象和引用

众所周知,jdk1.7中已经把字符串常量池从永久代中剥离出来,存放在堆空间中。
但是字符串常量池在堆中是怎样维护的,常量池中存的究竟是String对象的引用还是String对象?
我们都知道类似String str="xxx"String str=new String("xxx")的创建过程是不一样的,引用比较也会有所差异。
下面是结合jmap -histo pid简单测试的结果:

  • 还待补充
  • java version - jdk1.8
  • String中 private final char value[]-jdk1.8 -> private final byte value[]-jdk1.9
public class StringTest {
    public static void  main(String[] args){
        System.out.println("KKKKKKKK");
        
        //String对象个数 1532  ||  char[] 1942
        //下面依次新增语句查看java.lang.String和char[]中对象个数。
        
        String str1="xyz";    //String对象个数: 1533  ||  char[] 1943
        String str2="xyz";    //String对象个数: 1533  ||  char[] 1943
        String str3="aa"+"bb"+"cc"+"dd";    //String对象个数: 1534  ||  char[] 1944
        String str4=new String("oook");    //String对象个数: 1536  ||  char[] 1945
        String str5=new String("oook");    //String对象个数: 1537  ||  char[] 1945
        try{
            Thread.sleep(30000);
        }
        catch(InterruptedException ex){
			...
        }
    }
}
  1. 结果验证了以下结论:
  • 字符串常量池中存储的是char[]数组对象String对象的引用的对应关系,String对象的引用所指向的String对象创建在堆中
  • jmap -histo pid 中会包含常量池中的对象。
  • char[]数组对象只会在常量池中创建, String对象中的value引用指向的是常量池中的char[]。
  • String首先会根据字符值在常量池中维护的char[]列表中查找。
  • str1 常量池中创建了char[]数组对象String对象引用的对应,String对象引用指向的String对象创建在堆中,返回的是String对象引用
  • str2 找到常量池中的char[]数组对象String对象引用对应记录,直接返回的是String对象引用
  • str3 编译器做了优化。 常量池中创建了"aabbccdd"的char[]数组对象String对象引用的对应,String对象引用指向的String对象创建在堆中,返回的是String对象引用
  • str4 常量池中创建了char[]数组对象String对象引用的对应,String对象引用指向的String对象A创建在堆中。 然后在堆中新建String对象B,里面的value指向的是常量池中的char[]数组对象,返回该String对象B的引用。
  • str5 找到常量池中的char[]数组对象String对象引用对应记录。堆中新建String对象C,里面的value指向的是常量池中的char[]数组对象,返回该String对象C的引用。

最终堆中(包含字符常量池)的图示为:

后面再补充下更多的测试结果。
补充引用间是否相等的对比结果,引用是否指向同一个对象的对比结果。
补充使用反射修改String对象中value的值等情况下的场景结果。

相关文档:
深入解析String#intern

;