String & intern()方法在jdk6 7 8中区别与联系

截图笔记中这里的intern方法的使用时针对jdk1.6
结果和1.7中String#intern方法不同,可以参考文末中链接中相关的内容
另外:

  1. 在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代
  2. 在JDK1.7 字符串常量池被从方法区拿到了堆中,这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代
  3. 在JDK1.8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆, 运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间(Metaspace)

下面这部分内容主要是结合视频学习过程中实操的笔记记录,有兴趣可读,没兴趣直接跳过截图到intern方法比较吧!
在这里插入图片描述

1 对于常量字符串,在编译的时候就会将这个字符存入常量池
常量池中其实有一个table表对象维护所有的字符串

2 对于 new String(“abc”)的理解:
有种说法new String会在堆中和常量池中创建两个对象,这种说法其实没错
因为括号里面出现的"abc"其实相当于String s1 = "abc"的作用,这个对象就已经存入了常量池 但是如果出现new String(“a”) + new String(“b”) + new String(“c”) 这种,那么常量池只有a b c 三个对象,但是并没有abc这个对象。对于 new String(str1); 这个时候str1的所代表的对象是不会存入常量池的,而是存入堆中

3 对于 String test = “a” + “b” + “c”:
这个时候常量池里面其实只有一个对象,因为 a b c 它们在编译的时候就会生成一个abc字符串, 所以只有一个对象被创建反编译过后成了 String test = “abc”:
同理:String s = new String(“abc” + “def”);反编译成为了 String s = new String(“abcdef”);也就是创建了两个对象
但是:String s = new String(“abc” + str); 就不一样了

4 对于变量字符串参与+运算
String s = “abc” + str;
因为编译期间无法确认str,所以不能以常量来计算,这个时候就得使用new来操作
但是如果 str 是 final 类型,那么又不一样了,他就是一个常量了,以后代码中出现的str都会被它的字符串来代替

5 常量池保存的是字符串还是字符串的引用?
其实回答这个问题需要区分jdk版本
jdk<=6 : 那么常量池中肯定保存的都是字符串
jdk>=7 : 那么常量池中保存可以是字符串,也可以是引用

那什么时候会出现保存引用呢?
这就需要了解jdk7以后,常量池被移到堆中,并且intern方法机制有改变,这个方法
在jdk7以前,机制是看常量池中有没有指定的字符串,如果没有则复制一份字符串到
常量池中;jdk7以后,区别是如果常量池没有则会把堆中的字符串的引用复制一份到
常量池中。
所以字符串可以保存在常量池中,也可以保存在堆中;
jdk7以后,常量池中可以保存字符串,也可以保存字符串的引用。
在这里插入图片描述
在这里插入图片描述
其实从图2 就可以得出一些结论:
a 字符串其实可以存储在两个地方,池和堆中都有存储,否则也不会出现intern这个方法去常量池中找有没有这个字符串,那说明有的字符串根本就不在池中

  比如 new String(String.valueOf(123))

  new String("a") + new String("b")

  创建的对象都是在堆中

6 对于String.intern方法(这个方法只有new对象才能调用)
jdk<7:则是将字符串本身在常量池中查找,如果有,则返回常量池中的引用,没有则在常量池中新建字符串本身,然后返回常量池的引用
jdk>=7:则是将字符串本身在常量池中查找,如果有,则返回常量池中的引用,这里没有变化,如果没有则会将堆中的引用传递给常量池,然后返回这个指向堆的引用

7 对于不同的版本的jdk常量池的位置(方法区也被叫做永久带)

jdk6:常量池放在方法区(永久带)中

jdk7:常量池放在堆中,方法区中的其余的还在方法区(永久带)

jdk8+:常量池放在堆中,方法区(永久带)被取消,被放在元空间

可以使用jdk8测试:

jvm参数:-Xmx20m -Xms20m -XX:-UseGCOverheadLimit

这里的-XX:-UseGCOverheadLimit是关闭GC占用时间过长时会报的异常

运行下面代码:

  public static void main(String[] args) throws Throwable {
      List<String> list = new ArrayList<String>();
      int i=0;
      while(true){
         list.add(String.valueOf(i++).intern());
      }
}

异常信息是:java.lang.OutOfMemoryError: Java heap space

在这里插入图片描述
原文参考:
https://blog.csdn.net/q5706503/article/details/84640762
https://blog.csdn.net/baidu_31657889/article/details/52315902
https://blog.csdn.net/Liu_York/article/details/87862823
https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html
http://www.importnew.com/27694.html
http://blog.csdn.net/seu_calvin/article/details/52291082
http://www.importnew.com/31126.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值