JVM-字符串常量池与String.intern()方法

1、字符串常量池在堆区还是在运行时常量池呢?

(1)首先介绍 运行时常量池在永久代(Permanent Generation)还是在元空间(meta-space)?
参考《深入理解Java虚拟机》
在JDK1.6(包含)之前,方法区(包含类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据,运行时常量池是方法区的一部分,包含了编译器生成的字面量和符号引用)是在永久代中,不在堆空间。
在JDK1.7 ,将永久代中的字符串常量池、静态变量等移出,放到了堆空间。
在JDK1.8,将永久代中的剩余内容(主要是类型信息)全部移到了元空间,元空间是在直接内存上的,此时字符串常量池,静态变量还是在堆空间,并没有移回来。

(2)从上面可知,1.7(不包含)之前,字符串常量池在方法区,方法区在永久代
1.7(包含)之后,字符串常量池在堆空间

2.String.intern()方法

String.intern():首先去判断该字符串是否在常量池中存在,如果存在返回常量池中的字符串,如果在字符串常量池中不存在,先在字符串常量池中添加该字符串,然后返回引用地址

创建了几个对象?

在JDK1.6中,

String str2 = new String("abcd");//创建了两个对象,第一个对象“abc”字符串寸尺在常量池中,第二个对象在JAVA Heap堆中String对象。
JDK6和JDK7下intern的区别

代码一:

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);
}

打印结果是

jdk6 下false false
jdk7 下false true

代码二:

public static void main(String[] args) {
    String s = new String("1");
    String s2 = "1";
    s.intern();
    System.out.println(s == s2);
 
    String s3 = new String("1") + new String("1");
    String s4 = "11";
    s3.intern();
    System.out.println(s3 == s4);
}

打印结果为:

jdk6 下false false
jdk7 下false false

代码一和代码二在jdk6中的解释:
来自:https://www.cnblogs.com/wxgblogs/p/5635099.html
在这里插入图片描述注:图中绿色线条代表 string 对象的内容指向。 黑色线条代表地址指向。
在jdk6中,字符串常量池在Perm区,Perm区和堆区是完全分开的。
语句1:String s = new String(“1”);会在Perm的字符串常量池中生成对象"1",并且在堆区生成一个String对象,这个String对象的值是”11“,相当于现在有两份”11“,s指向堆区的这个String对象。因此s.intern()放到哪里都是无所谓的,或者放不放也无所谓,因为此时已经有”1“在字符串常量池中了。
语句2:String s2 = “1”;s2指向字符串常量池的”11“,因此s与s2是不同的引用。在jdk6和jdk7中都是false。

语句3:String s3 = new String(“1”) + new String(“1”);会在字符串常量池中创建一个"1",在创建s的时候常量池中已经有”1“了,所有这里就不再在字符串常量池中创建”1“了。还会再堆区创建一个String对象,s3引用指向堆区的这个String对象,此时并没有在字符串常量池中创建”11“。
语句4:s3.intern()方法在字符串常量池中创建了”11“字符串。
语句5:String s4 = “11”;s4指向字符串常量池中的”11“,而在语句3中可知,s3指向的堆区的对象,因此s3 != s4。
即使调换语句4和语句5,也s3和s4的指向依然没有改变。

代码一和代码二在jdk7中的解释:
在这里插入图片描述

关于s和s2解释同jdk6.
在第一段代码中,
语句1String s3 = new String("1") + new String("1");同jdk6的语句3:会在字符串常量池中创建一个"1",在创建s的时候常量池中已经有”1“了,所有这里就不再在字符串常量池中创建”1“了。还会再堆区创建一个String对象,s3引用指向堆区的这个String对象,此时并没有在字符串常量池中创建”11“。

语句2s3.intern();这个是与jdk6不同的地方

原链接的解释:
s3中的"11"字符串放入String 常量池中,因为此时常量池中不存在"11"字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个"11"的对象,关键点是 jdk7 中常量池不在Perm区域了,这块做了调整。常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向s3引用的对象。 也就是说引用地址是相同的。

我的理解:
这句话依然是将”11“放入字符串常量池,那么怎么放的呢?首先字符串常量池中是没有”11“字符串的,jdk7中字符串常量池在堆区,此时堆区就无需放两份”11“了(堆区一份,字符串常量池一份),这个字符串常量池直接存储堆中的引用,这个引用指向s3引用的对象,也就是引用地址是相同的。

语句3String s4 = "11"; s4引用就是字符串常量池中”11“的引用,而这个引用指向了堆区的”11“String对象,也就是s3引用指向的对象,因此s3 == s4 是 true。

在第二段代码中,
图片来源:https://www.cnblogs.com/wxgblogs/p/5635099.html(支持原创哦)
在这里插入图片描述
第二段代码交换了语句2,语句3,那么
语句2String s4 = "11"; 在字符串常量池中生成了”11“字符串,并且s4指向了常量池的”11“.
语句3s.intern();这句话就是在字符串常量池中是否存在”11“字符串,如果存在,则返回这个引用,如果不存在,则创建一个”11“字符串,并且返回引用。此时由于语句2,可知已经有了”11“字符串,因此它直接返回引用,没有做其他操作。
此时s3指向了堆区的String对象,s4指向的字符串常量池的”11“,他们是不同的引用,结果为false。

3.使用intern

见:https://www.cnblogs.com/wxgblogs/p/5635099.html
String.insern()适用于只有有限值,并且这些有限值会被重复利用的场景。
使用insern()方法,首先会去常量池中判断是否有重复的字符串,如果没有,则会生成,因此多了一个判断过程。
https://blog.csdn.net/u011635492/article/details/81048150

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值