美文网首页
在JDK6与JDK7中String的intern()具体发什么改

在JDK6与JDK7中String的intern()具体发什么改

作者: 小杰的快乐时光 | 来源:发表于2018-08-23 07:24 被阅读0次

参考文章: https://tech.meituan.com/in_depth_understanding_string_intern.html

首先我们来看看 intern 方法的具体说明

/**
* 返回字符串对象的规范表示形式。
* 字符串常量池,初始为空,由String类来维护
* 当intern方法被调用时,如果池中已经有一个字符串和传入的字符串相等(equals),
* 返回池中的字符串,否则,将这个String对象添加到池中并返回这个String对象的引用。
* 因此,对于任意两个字符串s和t,如果str1.equals(str2)则有str1.intern() == str2.intern()。
*/
public native String intern();

从修饰符上来看,我们知道 String#intern 方法是一个 native 的方法,作用是:如果常量池中已经有了这个字符串,那么直接返回常量池中它的引用,如果没有,那就将它的引用保存一份到字符串常量池,然后直接返回这个引用。返回值 是返回字符串引用。因此在创建大量字符串时,使用 String#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);
}

首先分析JDK1.6的情况:
因为 jdk6中的常量池是放在 Perm 区中的,Perm 区和正常的 JAVA Heap 区域是完全分开的。String s2 = "1";中s2指向的是Perm 区中的String Pool,而String s = new String("1");是指向Java堆中,所以无论怎么变,这两个都不会相等。
因此两个都输出false。

分析JDK1.7的情况:
背景:在 Jdk6 以及以前的版本中,字符串的常量池是放在堆的 Perm 区的,Perm 区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4m,一旦常量池中大量使用 intern 是会直接产生java.lang.OutOfMemoryError: PermGen space错误的。 所以在 jdk7 的版本中,字符串常量池已经从 Perm 区移到正常的 Java Heap 区域了。为什么要移动,Perm 区域太小是一个主要原因,当然据消息称 jdk8 已经直接取消了 Perm 区域,而新建立了一个元区域。应该是 jdk 开发者认为 Perm 区域已经不适合现在 JAVA 的发展了。

我们姑且将 new 创建的对象叫做二号本体,反之叫做 一号本体
①String s = new String("1"); 在堆中创建 "1"的一号本体,字面量为 "1",在字符串常量池中保存的是这个一号本体的引用;然后 new String 在堆中新创建 一个"1" 的二号本体,字面量仍然为"1",但是字符串常量池中并没有二号本体的引用,s指向的是二号本体;
② s.intern(); 将会去字符串常量池中检查有没有跟 "1"相同的字符串(通过引用查找,使用 equals 判断),发现有,是一号本体的引用,返回该引用但没有接收。
③ String s2 = "1"; 首先会检查字符串常量池中有没有"1"这个字符串(通过引用查找,使用 equals 判断),发现有,是一号本体的,那么s2会直接指向一号本体.
s 指向 二号本体 ,s2 指向一号本体,因此 s == s2 输出 false

⑤ String s3 = new String("1") + new String("1"); 由于堆中已有 "1" 一号本体的存在,因此class变量池中的这两个"1"并不会再加入运行时常量池中。因此这行代码创建了3个对象。两个new 创建的内容为"1"的二号本体,第三个是通过内部是创建了一个StringBuilder对象,一路append,最后调用StringBuilder对象的toString方法得到一个String对象(内容是11,注意这个toString方法会new一个String对象,类似于二号本体),并把它赋值给s3。在字符串常量池中并没有 "11"的引用。
⑥ s3.intern(); 将会去字符串常量池中检查有没有跟 "11"相同的字符串,发现没有,所以会将"11"的引用放入字符串常量池中,并返回该引用但没有接收,此时字符串常量池中的"11"引用是跟s3相同。
⑦String s4 = "11"; 发现堆中已经"11"字符串的引用了,会直接使用这个引用。
s3与s4都指向同一个 new 创建的对象,因此输出为 true

代码图示

String intern()

相关文章

网友评论

      本文标题:在JDK6与JDK7中String的intern()具体发什么改

      本文链接:https://www.haomeiwen.com/subject/lszuiftx.html