String类中intern方法的原理分析

Posted fenjyang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了String类中intern方法的原理分析相关的知识,希望对你有一定的参考价值。

一,前言

? 昨天简单整理了JVM内存分配和String类常用方法,遇到了String中的intern()方法。本来想一并总结起来,但是intern方法还涉及到JDK版本的问题,内容也相对较多,所以今天就弥补昨天缺失的知识点。

二,String.intern()

? 先来看下网上流行的关于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

首先我们先来回顾一个概念:

  • jdk6及以前中常量池是存放在方法区中。
  • jdk7以后常量池移动到堆中

? 为什么要移动?常量池主要是存储加载类的信息,编译器生成的字面量和符号引用等。它的大小大约只有4M,如果大量使用intern()方法,便会造成常量池内除溢出的情况,同时GC回收也有一定的困难度。而且在jdk8以后,连Perm区域都没有了,被替换成了元区域。

? 接着我们再来说说上面两种情况不同的结果。

JDK6:

? String s = new String("1");

? 1,这句代码实际上是生成两个实例对象,先在常量池中声明为1的字符对象,再通过关键字new生成1的字符对象并被s引用。

? 2,接着s.intern();先在常量池中查找是否有相应的字符串存在,如果有,直接返回引用,否则,在常量池中生成相应的字符串并返回引用

? 3,而String s2 = "1";是属于符号引用,直接在常量池中生成。

? 4,最后判断s 是否与s2相等,通过上面的分析,s指向的是通过关键字new出来的在堆中的字符对象,而s2是符号引用的在常量池中的字符对象,所以返回的结果为false。如下图所示:

? 技术图片

? String s3 = new String("1") + new String("1");同理这句代码实际上也是产生2个字符对象,其原理和上面分析是一样的,而最后判断s3与s4的值,结果还是一个堆中的s3与常量池中的s4比较,那返回的结果必然是false。

JDK7:

    String s1 = new String("1");
    s1.intern();
    String s2 = "1";
    System.out.println(s == s2);

? 1,这一部分代码与jdk6中的分析是类似的,但唯一不同的是s1.intern();返回的是在常量池中的引用,并不会在常量池中复制一份再返回引用

? String s2 = "1";s1.intern();指向的是同一个引用。

? 技术图片

? 2,最后比较s与s2的值,s是堆中,s2是常量池中,因而返回结果为false。

? 3,接着我们再来看下面一段代码:

        String s3 = new String("1") + new String("1");
        s3.intern();
        String s4 = "11";
        System.out.println(s3 == s4);

? 我们先来分析第一句代码,同样是生成两个字符对象,先在常量池中生成为1的字符对象,再在堆中生成s3为11的字符对象。接着s3.intern();执行便去常量池中查询是否存在为11的字符对象,但是发现没有。这个时候在jdk7及之后便不会再复制一份为11到常量池中,而是直接返回堆中11的字符对象的引用。所以它与s3的引用地址是相同的。

? String s4 = "11";这句代码是符号引用,直接去常量池中创建,但是发现池中已经存在为11的字符对象的引用,因此直接返回该引用。最后判断s3与s4是否相等,因为它们之间的引用的相同的,所以返回的结果为true。如下图所示:

技术图片

三,总结

? 1,对于常量池的概念要注意jdk6与jdk7之间的变化。

? 2,intern()方法在不同jdk版本之间的变化,jdk7之后如果常量池中不存在,不会再复制一份到常量池中,而是返回堆中的存在的引用地址。

? 3,jdk6常量池在方法区中,jdk7常量池在堆中,jdk8取消方法区,替换成元区域。

? 最后关于String类中intern方法就介绍这里,其实从实际开发工作中,我们只需要了解jdk7及以后版本的原理就可以了,毕竟现在市面上很少再用jdk6版本。但是这里总结出来也是为了更方便的参照理解,同时也涉及到一些关于JVM内存的相关知识,本篇博客是接着上一篇内容的缺失的知识点。

? 以上内容均是自主学习总结的,如有不适之处还请留言(邮箱)指教。

感谢阅读!

以上是关于String类中intern方法的原理分析的主要内容,如果未能解决你的问题,请参考以下文章

Java必修课String.intern()原来还能这么用(原理与应用)

Java必修课String.intern()原来还能这么用(原理与应用)

Lcom/google/android/gms/common/internal/Preconditions 类中没有静态方法 checkHandlerThread(Landroid/os/Handle

深入解析String.intern()方法

第一章 关于String的一些

JDK方法区元空间以及String.intern()知识要点