为了您更好的理解本篇文章,请先查阅
https://blog.csdn.net/define_us/article/details/78252783 中的JVM分代管理策略一节。
JDK6
JDK的String常量池存在于Perm区,和JAVA Heap区是完全不同的两个区域。通过字符串常量生成的String对象存在在永久代上,而通过new产生的对象,分配在JAVA HEAP上。intern会查看是否有String常量池的值等于当前字符串,如果没有,把在常量池新建该值的字符串对象。然后把常量池中的字符串对象返回。
public static void main(String[] args) {
//指向常量池中的同一个字符串对象
String s5 = "111";
String s6 = "111";
System.out.println(s5 == s6);//true
//s8是intern后返回的常量池中的字符串对象
String s7 = "111";
String s8 = new String("111").intern();
System.out.println(s7 == s8);//true
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);//false
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);//false
}
- JDK6有一个硬伤,就是subString的内存泄漏问题
如下代码:
String str = "abcdefghijklmnopqrst";
String sub = str.substring(1, 3) + "";
str = null;
运行结束后str内部的char数组不会被回收,因为substring是通过调整边界实现的。在str被置为null后,sub内部仍然引用者char数组。
public String substring(int var1, int var2) {
return var1 == 0 && var2 == this.count ?
this : new String(this.offset + var1, var2 - var1, this.value);//这里复用了当前字符串的value数组。
}
String(int var1, int var2, char[] var3) {
this.value = var3;
this.offset = var1;
this.count = var2;
}
采用如下方式就可以解决该问题。
String str = "abcdefghijklmnopqrst";
String sub = str.substring(1, 3) + "";
str = null;
JDK8
在JDK8中运行上述代码,结果变成了
true
true
false
true