StringTable学习笔记

对于StringTable初步理解

 参考博客:https://blog.csdn.net/qq_26632895/article/details/105587461

一.什么是StringTable

StringTable是字符串常量池,又称为String pool,它是存在于堆中的一块区域(jdk1.7版本之后),每次当我们声明字符串的时候,都会将其放入字符串常量池中。但是在字符串常量池中储存的并不是String类型的对象,而是指向String对象的索引,真实的String对象其实还是存储在堆中的。

比如说,String s1 = "a";这样子声明的字符串"a"会被放入字符串常量池中,String s1 = new String("a");这样子会在堆中创建一个"a"的String类对象,这两者是不同的。

例如:

String s2 = new String("a");
String s1 = "a";
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);

运行结果:

 

 可以得出结论,这两个"a"尽管内容相同,但是地址却是不同的

 

二.StringTable的作用和特点

  • StringTable可以避免创建重复字符串对象,减少内存的开销
  • 字符串延迟加载。字符串常量池中的字符串刚开始只是符号,只有被使用的时候才会被转换成对象
  • 字符串常量池中相同的字符串只能存在一个,不存在重复的字符串,它是hash表结构

三.字符串拼接原理

1.字符串变量拼接

字符串变量拼接的原理是通过new一个StringBuilder对象,然后调用它的append方法将字符串拼接起来,最后调用toString()方法,将其转化为String类型的对象。

也就是说,

String s3 = "a" + "b";

相当于

String s3 = new StringBuilder().append("a").append("b").toString();

我们可以将java代码编译后的文件反编译后,查看它的jvm指令

源代码:

String str1 = "abc";
String str2 = "ab" + "c";
String str3 = new String("ab") + "c";

在控制台进到源代码所在的根目录,输入指令

javac 文件名.java

然后输入指令

javap -varbose 文件名.class

查看jvm指令如下:

 

 

2.字符串常量拼接

字符串常量进行拼接的时候,jvm会自动进行编译期优化,由于结果唯一,jvm会将两个字符串常量拼接后再去字符串常量池中找相同的字符串,如果有就会从常量池中拿,没有就会王往里面添加。

四.关于字符串的延迟加载

字符串常量池中的字符串刚开始只是符号,只有被使用的时候才会被转换成对象,可以通过idea查看memory来验证这个特性

 

 轮流输出8个字符串,使用idea的debug功能进行断点调试

 

 执行前,idea中memory显示String类有2707个,执行前两句代码后,

 

 可以明显看到,String类的数量增加了两个

五.StringTable的性能调优

1.通过调整jvm参数

StringTable的底层是一个哈希表,所以我们可以通过增加桶的数量,来提升效率

-XX:StringTableSize=桶个数

2.通过intern()方法将字符串入池

如果是堆中的字符串对象有较多重复,可以使用intern()入池来减少内存占用

  1. 在jdk1.6中,使用intern()会尝试将这个字符串对象放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池,把串池中的对象返回,这时候原来字符串的位置并不会改变
  2. 在jdk1.8中,使用intern()会尝试将这个字符串对象放入串池,如果有则并不会放,如果没有则会把该对象放入串池,把串池中该对象的引用返回,这时候原来的字符串位置已经到了串池中

六.案例分析

 

 第七行输出false,因为s3是字符串常量相加,因为编译期优化,拿的是串池中的"ab",s4是两个字符串变量相加,最后结果是在堆里new出一个新的字符串对象,所以两者地址不相同。

第八行输出true,两者都是字符串常量相加,最后拿的都是串池中的"ab",所以地址相同

第九行输出true,s4执行了intern方法拿的是串池中的"ab",所以s6指向串池中的"ab",s3也指向串池中的"ab",所以两者地址相同

第十四行输出false,x2执行intern()但是没有改变x2本身,x2指向堆中new出来的"cd"字符串对象,x1拿的是串池中的"cd",所以两者地址不相同

因为x2本身指向的地址没变,所以调换最后两行代码结果不变,还是false,jdk1.6中也是false

如果改成x2 = x2.intern();

那么jdk1.8中为true,jdk1.6中也是true,因为返回的都是串池中的字符串

posted @ 2021-03-31 17:49  TidalCoast  阅读(215)  评论(0编辑  收藏  举报