jdk1.8之後的String.intern()方法內存分析

關於String.intern()方法,這個問題都被問爛了,jdk1.8之後內存模型發生了變化,內存的變化也會影響intern方法的執行,這裏有必要寫文章分析一下,請大家務必從頭開始看,這樣才能搞懂

1.字符串常量池劃分

jvm對字符串常量池在不同jkd版本有不同的劃分,這裏用hotspot來分析,文章後部分會使用,主要有以下三種方式
在這裏插入圖片描述
大致劃分爲這幾個部分,對方法區,元空間和堆等概念模糊的朋友可以參考方法區,永久代,元空間這篇文章

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[];

在這裏插入圖片描述
簡要分析一下:
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.6以後常量池被放置在了堆空間,JDK1.8又將常量池劃分回方法區,這裏的方法區的實現是元空間

jdk1.6實現方式

在這裏插入圖片描述
通過圖片分析:
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.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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章