1. 程式人生 > >String類中intern方法的原理分析

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記憶體的相關知識,本篇部落格是接著上一篇內容的缺失的知識點。

​ 以上內容均是自主學習總結的,如有不適之處還請留言(郵箱)指教。

感謝閱讀