申明 : 此文仅仅作为个人学习使用 , 如果有人对于String.intern() 十分想要究其原理 , 请参考此文深入解析String#intern - 美团技术团队
8种基本类型的常量池都是系统协调的,String类型的常量池比较特殊。它的主要使用方法有两种:
直接使用双引号声明出来的
String对象会直接存储在常量池中。如果不是用双引号声明的
String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中’
native 代码解析
大体实现结构是 : Java 使用 jni 调用 c++ 实现的 StringTable 的 intern 方法, StringTable的 intern 方法跟 Java 中的 HashMap 的实现是差不多的, 只是不能自动扩容。默认大小是1009。
要注意的是,String 的 String Pool 是一个固定大小的 Hashtable,默认值大小长度是1009,如果放进 String Pool 的 String 非常多,就会造成 Hash 冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用 String.intern 时性能会大幅下降(因为要一个一个找)。
因此在jdk7中进行了改进,StringTable的长度可以通过一个参数指定:
-XX:StringTableSize=99991
看段代码
public static void main(String[] args) {
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
//jdk 6 false false
//jdk 7 false true
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sY9GM0DJ-1650113024620)(picture/1.png)]](https://i-blog.csdnimg.cn/blog_migrate/f00b689fec33042d4ac417a04da949a8.png)
注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向。
在 jdk6中上述的所有打印都是 false 的,**因为 jdk6中的常量池是放在 Perm 区中的,Perm 区和正常的 JAVA Heap 区域是完全分开的。上面说过如果是使用引号声明的字符串都是会直接在字符串常量池中生成,而 new 出来的 String 对象是放在 JAVA Heap 区域。**所以拿一个 JAVA Heap 区域的对象地址和字符串常量池的对象地址进行比较肯定是不相同的,即使调用String.intern方法也是没有任何关系的。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jFsP2oKv-1650113024620)(picture/2.png)]](https://i-blog.csdnimg.cn/blog_migrate/f4f302ebc9937e321da7643840f8a9c9.png)
在 jdk7 的版本中,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了。
-
先看
s3和s4字符串。String s3 = new String("1") + new String("1");,这句代码中现在最终生成了2个对象,是字符串常量池中的“1” 和 JAVA Heap 中的s3引用指向的对象。中间还有2个匿名的new String("1")我们不去讨论它们。此时s3引用对象内容是”11”,但此时常量池中是没有 “11”对象的。 -
接下来
s3.intern();这一句代码,是将 s3中的“11”字符串放入 String 常量池中,因为此时常量池中不存在“11”字符串,因此常规做法是跟jdk6图中表示的那样,在常量池中生成一个 “11” 的对象,关键点是jdk7中常量池不在 Perm 区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向s3引用的对象。 也就是说引用地址是相同的。
了,可以直接存储堆中的引用。这份引用指向s3引用的对象。 也就是说引用地址是相同的。 -
最后
String s4 = "11";这句代码中”11”是显示声明的,因此会直接去常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向s3引用对象的一个引用。所以s4引用就指向和s3一样了。因此最后的比较s3 == s4是 true。
深入解析Java String#intern()方法及其内存优化
本文详细探讨了Java中的String常量池,解释了直接使用双引号声明和通过intern()方法进入常量池的区别。在JDK6与JDK7中,由于常量池位置的变化,intern()方法的行为有所不同。在JDK7中,由于常量池移至堆区,intern()可能导致堆内引用复用,提高性能。文章通过实例代码展示了不同版本JDK下的行为差异,并提到了通过-XX:StringTableSize参数调整常量池大小以优化性能。
2136

被折叠的 条评论
为什么被折叠?



