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

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

  • 在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代
  • 在JDK1.7 字符串常量池被从方法区拿到了堆中,这里没有提到运行时常量池,也就是说字符串常量池被单独拿到堆,运行时常量池剩下的东西还在方法区, 也就是hotspot中的永久代
  • 在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")
  1. 对于String.intern方法(这个方法只有new对象才能调用)
    jdk<7:则是将字符串本身在常量池中查找,如果有,则返回常量池中的引用,没有则在常量池中新建字符串本身,然后返回常量池的引用
    jdk>=7:则是将字符串本身在常量池中查找,如果有,则返回常量池中的引用,这里没有变化,如果没有则会将堆中的引用传递给常量池,然后返回这个指向堆的引用

  2. 对于不同的版本的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/wodemale/article/details/89350095

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值