秋招了,为学弟硬肝的字符串常量池讲解,冲~

字符串常量池

在JAVA语言中有8种基本数据类型和String。为了使它们在运行过程中速度更快、更节省内存,提供了对应的常量池。常量池就类似于一个JAVA系统级别提供的缓存。8种数据类型的常量池都是系统协调的,String类型的常量池(StringTable)比较特殊。它的使用方式有两种:

  • 直接使用双引号声明,此String对象会直接存储在常量池中,比如:String str = “Hello World”;
  • 使用String提供的intern()方法将字符串放入常量池中。

常量池位置变化

  • JDK6以前,字符串常量池存放在永久代
  • JDK7中将字符串常量池的位置调整到了JAVA堆中
    • 所有的字符串都保存在堆中,这样我们在调优时仅仅需要调整堆的大小即可

image.png

image.png

为什么调整?

  • permSize默认大小比较小
  • 永久代垃圾回收频率低

StringTable特性

  • 字符串常量池中不会存储相同内容的字符串
  • 字符串常量池是一个固定大小的Hashtable,默认长度为1009。如果放入常量池中的String非常多就会造成严重的哈希冲突,从而导致链表变很长(拉链法解决哈希冲突),这会直接导致调用intern方法时的性能大幅度下降
  • 使用-XX:StringTableSize可设置StringTable的长度
  • 在JDK6中StringTable长度是固定的,为1009,所以如果常量池中的字符串过多就会导致效率下降很快。我们可以随意设置其长度
  • 在JDK7中,StringTable的长度默认为60013,从JDK8开始,1009是可设置的最小值

谜一样的拼接操作

在考察字符串相关知识点时字符串的拼接一直是重点和难点,我们不知道这是新建对象还是放入了常量池中。首先给出以下结论:

  • 常量与常量的拼接结果在常量池中,原理是编译期优化
  • 常量池中不会存在相同内容的常量
  • 只要其中有一个是变量,结果就在堆中。拼接原理是StringBuilder
  • 如果拼接的结果调用intern方法,则主动将常量池中还没有的字符串对象放入常量池中,并返回对象地址

验证一

image.png
查看对应字节码文件:

image.png

ldc: 将常量池中的数据压入操作数栈

借助IDEA的反编译插件我们可以发现字符串的拼接被优化了:

image.png

验证二

image.png
不难发现在第二次打印字符串时整个类中的String对象并未增加,说明常量池中只存在唯一的一个字符串。

验证三

image.png
查看对应字节码文件:

image.png
str2的拼接操作是通过生成一个StringBuilder进行append实现的。这里会涉及到一个经典面试题:【new String(“a”)+new String(“b”)共生成几个字符串?】

注意使用final修饰的变量:

image.png
查看对应字节码文件:

image.png
使用final修饰的变量的拼接依旧会被编译器优化哦~

深入理解intern()

如果常量池中存在当前字符串,就会直接返回当前字符串。如果常量池中没有此字符串,会将此字符串放入常量池中后,再返回对象引用。很遗憾,这是一个native方法,我们可以通过查看OpenJdk7的源码一探究竟。

image.png

image.png

创建了几个对象?

经典面试题:new String(“ab”)会创建几个对象?

答案是两个。但是,许多同学不知道为什么?该如何证明呢?看字节码即可啦~~

image.png

不难发现创建了两个对象,一个是new出来的对象,还有一个是常量池中的ab(ldc #3 )。

拓展:new String(“a”)+new String(“b”)呢?

image.png

单纯从字节码可以发现创建了5个对象:

  • new StringBuilder();
  • new String(“a”);
  • 常量池中的”a”;
  • new String(“b”);
  • 常量池中的”b”;

注意StringBuilder的toString方法:

image.png

天真的你也许会说我知道了,还有两个对象:

  • new String(“ab”);
  • 常量池中的”ab”;

image.png

查看toString方法的字节码:

image.png

强调:toString的调用在常量池中没有生成”ab”。

JDK6 VS JDK7?

经典问题:请问下方代码在JDK6和JDK7中的运行结果是什么?

image.png

  • JDK6中:false false
  • JDK7中:false true

讲解开始

image.png

对应内存情况如下图所示:

image.png

拓展

image.png

仅仅将s3.intern()和s4变换了位置,答案已经变为false了。

讲解开始

image.png
对应内存情况如下图所示:

image.png

​intern总结

  • JDK1.6中,将这个字符串对象尝试放入字符串常量池
    • 如果常量池中有则不会放入,返回已有的对象地址
    • 如果没有会把此对象复制一份(深拷贝),放入常量池,并返回对象地址
  • 自JDK1.7起,将这个字符串对象尝试放入字符串常量池
    • 如果常量池中有则不会放入,返回已有的对象地址
    • 如果没有,会把对象的引用地址复制一份,放入常量池,并返回对象地址

intern习题

image.png

image.png

上方两题在不同JDK版本中的运行结果是什么呢?

image.png

image.png
那么这两题在同一JDK版本中会有不同吗?

欢迎评论区回答哦!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享