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()入池来减少内存占用
- 在jdk1.6中,使用intern()会尝试将这个字符串对象放入串池,如果有则并不会放入,如果没有会把此对象复制一份, 放入串池,把串池中的对象返回,这时候原来字符串的位置并不会改变
- 在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,因为返回的都是串池中的字符串