jdk1.8之后的String.intern()方法内存分析

关于String.intern()方法,这个问题都被问烂了,jdk1.8之后内存模型发生了变化,内存的变化也会影响intern方法的执行,这里有必要写文章分析一下,请大家务必从头开始看,这样才能搞懂

1.字符串常量池划分

jvm对字符串常量池在不同jkd版本有不同的划分,这里用hotspot来分析,文章后部分会使用,这里需要说明一下,jdk7以后常量池被划分到堆空间去了,但在java虚拟机规范中提到,类的信息,常量等都该被划分到方法区,而方法区也是堆的一个逻辑部分,而为了于堆区分也叫它非堆,java虚拟机并没有要求如何实现方法区,jdk8及以后用元空间代替永久代实现方法区,对方法区,元空间和堆等概念模糊的朋友可以参考方法区,永久代,元空间这篇文章

2.回顾string对象在内存中的位置以及不可变性

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

字符串是不可变的,public final class String
字符串是利用其内部value字符数组来存储的
表示字符串的字符数组也是不可变的:private final char value[];

jdk1.8之后的String.intern()方法内存分析_第1张图片
简要分析一下:
str1str2都是在栈上创建的字符串引用,不同的是str2是通过new关键字创建的在堆上的String对象,str1直接创建的是字面量abcstr2通过在堆上创建String对象,而string对象的内部属性value数组是一个对象引用,指向常量池中的abc,常量池中的abc也可以理解为是一个string类中不可变的字符数组

3.intern()

有了上面的预备知识,我们开始分析intern()这个方法,这里列出网上比较热门的代码来分析:

public static void main(String[] args) {
    String s1 = new String("1");
    s1.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中返回false,false,jdk1.7是false,true

在我们开始分析之前,我们回到第一步,先对jvm内存划分有一个了解
JDK1.7把常量池被放置在了堆空间,在逻辑上属于方法区,jdk1.8方法区的实现是元空间

jdk1.6实现方式

jdk1.8之后的String.intern()方法内存分析_第2张图片
通过图片分析:
String s3 = new String("1") + newString("1"),这行代码在字符串常量池中生成“1” ,并在堆空间中生成s3引用指向的对象(内容为"11")。注意此时常量池中是没有 “11”对象的。

s3.intern(),这一行代码,是将 s3中的“11”字符串放入 String 常量池中,此时常量池中不存在“11”字符串,JDK1.6的做法是直接在常量池中生成一个 “11” 的对象。

但是在JDK1.7中,常量池中不需要再存储一份对象了,可以直接存储堆中的引用。这份引用直接指向 s3 引用的对象,也就是说s3.intern() ==s3会返回true。

String s4 = "11", 这一行代码会直接去常量池中创建,但是发现已经有这个对象了,此时也就是指向 s3 引用对象的一个引用。因此s3 == s4返回了true。

jdk1.7实现方式

jdk1.8之后的String.intern()方法内存分析_第3张图片

总结

虽然jdk1.7以后将常量池转移到了方法区中,但intern的工作原理却并没有改变,说到这里,我们再来通过一个案例来检验一下你是否真的理解了intern的作用

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

答案是:

false
false
  • s1已经在常量池中创建了"1",s1.intern();的作用是徒劳的,返回的是堆中的地址,而s2返回的是常量池中的地址,结果当然为false
  • s4抢先一步在常量池中创建了字面量"11",并且s3只在常量池中创建了"1",所以s3.intern()返回的是常量池的"11"的地址,s3当然不等于s4,返回false

友情链接:
链接1
链接2

你可能感兴趣的:(精华帖)