JDK7 中将StringPool从永久代移动到了老生代,并且可以通过-XX:StringTableSize=99991来改变常量池的大小。
public void equalTest(){
String s = new String("1").intern();
String s2 = "1";
System.out.println(s2 == s);
}
output: true
public void equalTest(){
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s2 == s);
}
output: false
jdk7下面第一段代码打印的是true,第二段打印的是false.
这是因为将常量池从永久的移动到老生代之后,intern()的意思是将该变量放到常量池中,因为new String("1")已经是在堆中了,所以会直接将其引用放入常量池中,这样当s2="1"语句直接去常量池中查找对象时得到的其实就是s所指向的对象。
第二段代码中,new String("1); 会生成两个对象一个是s指向的Object String,一个是常量池中的对象; 然后执行s.intern()的时候发现常量池中已经有该对象了,所以什么也不做。s2则指向常量池中的对象。因此s != s2
我们再来看下面这段代码:
public static void main(String[] args){
int length = 100000000;
String[] s = new String[length];
int i = 0;
while(i < length){
s[i++] = new String("hellosdfasfsdddddddddddddd").intern();
}
}
new String("hellosdfasfsdddddddddddddd").intern() 这句话的意思是在常量池中new一个对象,所以当我们第一次执行常量池中有了这个对象之后,后面就不会再new新对象了,大大节约了内存空间。
下面是有intern和没有intern的堆内存使用情况,大家感受一下
s[i++] = new String("hellosdfasfsdddddddddddddd").intern();
PS Old Generation
capacity = 1431830528 (1365.5MB)
used = 400008208 (381.47755432128906MB)
free = 1031822320 (984.0224456787109MB)
27.936840301815383% used
s[i++] = new String("hellosdfasfsdddddddddddddd");
PS Old Generation
capacity = 1431830528 (1365.5MB)
used = 1431412656 (1365.1014862060547MB)
free = 417872 (0.3985137939453125MB)
99.97081554053861% used