天天看點

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

1.String的基本特性

  • String:字元串,使用一對""引起來表示。

    – String sl = “hello”;//字面量的定義方式

    – String s2 = new String(“hello”) ;

  • String類聲明為final的, 不可被繼承
  • String實作了Serializable接口:表示字元串是支援序列化的。
  • 實作了Comparable接口:表示String可以比較大小
  • String在jdk8及以前内部定義了final char[],value用于存儲字元串資料。jdk9時改為byte[]

結論: String再也不用char[] 來存儲啦,改成了byte[] 加上編碼标記,節約了一些空間。StringBuffer和StringBuilder也做了一些修改

public final class String implements java.io.Serializable, Comparable<String>,CharSequence {
@Stable
private final byte[] value;
}

           
  • String:代表不可變的字元序列。簡稱:不可變性。

    – 當對字元串重新指派時,需要重寫指定記憶體區域指派,不能使用原有的value進行指派。

    – 當對現有的字元串進行連接配接操作時,也需要重新指定記憶體區域指派,不能使用原有的value進行指派。

    – 當調用String的replace()方法修改指定字元或字元串時,也需要重新指定記憶體區域指派,不能使用原有的value進行指派。

  • 通過字面量的方式(差別于new)給一個字元串指派,此時的字元串值聲明在字元串常量池中。
/**
 * String的基本使用:展現String的不可變性
 */
public class StringTest1 {
    @Test
    public void test1() {
        String s1 = "abc";//字面量定義的方式,"abc"存儲在字元串常量池中
        String s2 = "abc";
        s1 = "hello";

        System.out.println(s1 == s2);//判斷位址:true  --> false

        System.out.println(s1);//
        System.out.println(s2);//abc

    }

    @Test
    public void test2() {
        String s1 = "abc";
        String s2 = "abc";
        s2 += "def";
        System.out.println(s2);//abcdef
        System.out.println(s1);//abc
    }

    @Test
    public void test3() {
        String s1 = "abc";
        String s2 = s1.replace('a', 'm');
        System.out.println(s1);//abc
        System.out.println(s2);//mbc
    }
}


           
  • 字元串常量池中是不會存儲相同内容的字元串的。

    – String的String Pool 是一個固定大小的Hashtable,預設值大小長度是1009。如果放進StringPool的String非常多, 就會造成Hash沖突嚴重,進而導緻連結清單會很長,而連結清單長了後直接會造成的影響就是當調用String. intern時性能會大幅下降。

    – 使用一XX: StringTableSize可設定StringTable的長度

    – 在jdk6中StringTable是固定的,就是1009的長度,是以如果常量池中的字元串過多就會導緻效率下降很快。StringTableSize設 置沒有要求

    – 在jdk7中,StringTable的長度預設值是60013

    – jdk8開始,1009是StringTable長度可設定的最小值

2.String的記憶體配置設定

  • 在Java語言中有8種基本資料類型和一種比較特殊的類型String。這些類型為了使它們在運作過程中速度更快、更節省記憶體,都提供了一種常量池的概念。
  • 常量池就類似一.個Java系統級别提供的緩存。8種基本資料類型的常量池都是系統協調的,String類 型的常量池比較特殊。它的主要使用方法有兩種。
  • 直接使用雙引号聲明出來的String對象會直接存儲在常量池中。

    –比如: String info = “abc” ;

    – 如果不是用雙引号聲明的String對象,可以使用String提供的intern()方法。這個後面重點談

  • Java 6及以前,字元串常量池存放在永久代。
  • Java 7中Oracle的工程師對字元串池的邏輯做了很大的改變,即将字元串常量池的位置調整到Java堆内。

    – 所有的字元串都儲存在堆(Heap)中,和其他普通對象一樣,這樣可以讓你在進行調優應用時僅需要調整堆大小就可以了。

    – 字元串常量池概念原本使用得比較多,但是這個改動使得我們有足夠的理由讓我們重新考慮在Java 7中使用String. intern()。

  • Java8元空間,字元串常量在堆

StringTable為什麼要調整

①永久代permSize預設比較小;

②永久代的垃圾回收頻率低;

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

3.String的基本操作

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
class Memory {
    public static void main(String[] args) {//line 1
        int i = 1;//line 2
        Object obj = new Object();//line 3
        Memory mem = new Memory();//line 4
        mem.foo(obj);//line 5
    }//line 9

    private void foo(Object param) {//line 6
        String str = param.toString();//line 7
        System.out.println(str);
    }//line 8
}

           
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

4.字元串拼接操作

  • 1.常量與常量的拼接結果在常量池,原理是編譯期優化
@Test
    public void test1(){
        String s1 = "a" + "b" + "c";//編譯期優化:等同于"abc"
        String s2 = "abc"; //"abc"一定是放在字元串常量池中,将此位址賦給s2
        /*
         * 最終.java編譯成.class,再執行.class
         * String s1 = "abc";
         * String s2 = "abc"
         */
        System.out.println(s1 == s2); //true
        System.out.println(s1.equals(s2)); //true
    }


           
  • 2.常量池中不會存在相同内容的常量。
  • 3.隻要其中有一個是變量,結果就在堆中。變量拼接的原理是StringBuilder
  • 4.如果拼接的結果調用intern()方法,則主動将常量池中還沒有的字元串對象放入池中,并傳回此對象位址。
package com.wfg.stringtable;

import org.junit.Test;

/**
 * java
 *
 * @Title: com.wfg.stringtable
 * @Date: 2020/9/9 18:11
 * @Author: wfg
 * @Description:
 * @Version:
 */
public class Demo3 {
    @Test
    public void test1(){
        String s1 = "a" + "b" + "c";//編譯期優化:等同于"abc"
        String s2 = "abc"; //"abc"一定是放在字元串常量池中,将此位址賦給s2
        /*
         * 最終.java編譯成.class,再執行.class
         * String s1 = "abc";
         * String s2 = "abc"
         */
        System.out.println(s1 == s2); //true
        System.out.println(s1.equals(s2)); //true
    }



    @Test
    public void test2(){
        String s1 = "javaEE";
        String s2 = "hadoop";

        String s3 = "javaEEhadoop";
        String s4 = "javaEE" + "hadoop";//編譯期優化
        //如果拼接符号的前後出現了變量,則相當于在堆空間中new String(),具體的内容為拼接的結果:javaEEhadoop
        String s5 = s1 + "hadoop";
        String s6 = "javaEE" + s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);//true
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//false
        System.out.println(s3 == s7);//false
        System.out.println(s5 == s6);//false
        System.out.println(s5 == s7);//false
        System.out.println(s6 == s7);//false
        //intern():判斷字元串常量池中是否存在javaEEhadoop值,如果存在,則傳回常量池中javaEEhadoop的位址;
        //如果字元串常量池中不存在javaEEhadoop,則在常量池中加載一份javaEEhadoop,并傳回次對象的位址。
        String s8 = s6.intern();
        System.out.println(s3 == s8);//true
    }


}

           

圖示

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
@Test
    public void test3(){
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        /*
        如下的s1 + s2 的執行細節:(變量s是我臨時定義的)
        ① StringBuilder s = new StringBuilder();
        ② s.append("a")
        ③ s.append("b")
        ④ s.toString()  --> 約等于 new String("ab")

        補充:在jdk5.0之後使用的是StringBuilder,
        在jdk5.0之前使用的是StringBuffer
         */
        String s4 = s1 + s2;//
        System.out.println(s3 == s4);//false
    }

    /*
    1. 字元串拼接操作不一定使用的是StringBuilder!
       如果拼接符号左右兩邊都是**字元串常量或常量引用**,則仍然使用編譯期優化,即非StringBuilder的方式。
    2. 針對于final修飾類、方法、基本資料類型、引用資料類型的量的結構時,能使用上final的時候建議使用上。
     */
    @Test
    public void test4(){
        final String s1 = "a";
        final String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2;
        System.out.println(s3 == s4);//true
    }
    
    //練習:
    @Test
    public void test5(){
        String s1 = "javaEEhadoop";
        String s2 = "javaEE";
        String s3 = s2 + "hadoop";
        System.out.println(s1 == s3);//false

        final String s4 = "javaEE";//s4:常量
        String s5 = s4 + "hadoop";
        System.out.println(s1 == s5);//true

    }
    

           
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

4.1 拼接操作與append的效率對比

append效率要比字元串拼接高很多

體會執行效率:通過StringBuilder的append()的方式添加字元串的效率要遠高于使用String的字元串拼接方式!

詳情:① StringBuilder的append()的方式:自始至終中隻建立過一個StringBuilder的對象

使用String的字元串拼接方式:建立過多個StringBuilder和String的對象

② 使用String的字元串拼接方式:記憶體中由于建立了較多的StringBuilder和String的對象,記憶體占用更大;如果進行GC,需要花費額外的時間。

**改進的空間**:在實際開發中,如果基本确定要前前後後添加的字元串長度不高于某個限定值highLevel的情況下,建議使用構造器執行個體化:
           StringBuilder s = new StringBuilder(highLevel);//new char[highLevel]
           
/*
    體會執行效率:通過StringBuilder的append()的方式添加字元串的效率要遠高于使用String的字元串拼接方式!
    詳情:① StringBuilder的append()的方式:自始至終中隻建立過一個StringBuilder的對象
          使用String的字元串拼接方式:建立過多個StringBuilder和String的對象
         ② 使用String的字元串拼接方式:記憶體中由于建立了較多的StringBuilder和String的對象,記憶體占用更大;如果進行GC,需要花費額外的時間。

     改進的空間:在實際開發中,如果基本确定要前前後後添加的字元串長度不高于某個限定值highLevel的情況下,建議使用構造器執行個體化:
               StringBuilder s = new StringBuilder(highLevel);//new char[highLevel]
     */
    @Test
    public void test6(){

        long start = System.currentTimeMillis();

//        method1(100000);//4014
        method2(100000);//7

        long end = System.currentTimeMillis();

        System.out.println("花費的時間為:" + (end - start));
    }

    public void method1(int highLevel){
        String src = "";
        for(int i = 0;i < highLevel;i++){
            src = src + "a";//每次循環都會建立一個StringBuilder、String
        }
//        System.out.println(src);

    }

    public void method2(int highLevel){
        //隻需要建立一個StringBuilder
        StringBuilder src = new StringBuilder();
        for (int i = 0; i < highLevel; i++) {
            src.append("a");
        }
//        System.out.println(src);
    }

           

5.intern()的使用

如果不是用雙引号聲明的String對象,可以使用String提供的intern方法: intern方法會從字元串常量池中查詢目前字元串是否存在,若不存在就會将目前字元串放入常量池中。

比如: String myInfo = new String(“I love u”).intern();

也就是說,如果在任意字元串上調用String. intern方法,那麼其傳回結果所指向的那個類執行個體,必須和直接以常量形式出現的字元串執行個體完全相同。是以,下 清單達式的值必定是true:

(“a” + “b” + “c”).intern()== “abc”;

通俗點講,Interned String就是確定字元串在記憶體裡隻有一份拷貝,這樣可以節約記憶體空間,加快字元串操作任務的執行速度。注意,這個值會被存放在字元串内部池(String Intern Pool)。

5.1 new String(“ab”)會建立幾個對象,new String(“a”)+new String(“b”)呢

public class StringNewTest {

    public static void main(String[] args) {
        String str = new String("ab");

        //String str = new String("a") + new String("b");
    }


}
           
  • new String(“ab”)會建立幾個對象?看位元組碼,就知道是兩個。

    一個對象是:new關鍵字在堆空間建立的

    另一個對象是:字元串常量池中的對象"ab"。 位元組碼指令:ldc

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
  • new String(“a”) + new String(“b”)呢?

對象1:new StringBuilder()

對象2: new String(“a”)

對象3: 常量池中的"a"

對象4: new String(“b”)

對象5: 常量池中的"b"

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

深入剖析: StringBuilder的toString():

對象6 :new String(“ab”)

強調一下,toString()的調用,在字元串常量池中,沒有生成"ab"

StringBuilder 的toString方法

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

5.2 關于String.intern()的面試題

**
 * 如何保證變量s指向的是字元串常量池中的資料呢?
 * 有兩種方式:
 * 方式一: String s = "shkstart";//字面量定義的方式
 * 方式二: 調用intern()
 *         String s = new String("shkstart").intern();
 *         String s = new StringBuilder("shkstart").toString().intern();
 *
 */
public class StringIntern {
    public static void main(String[] args) {
        String s = new String("1");
        String s1 = s.intern();//調用此方法之前,字元串常量池中已經存在了"1"
        String s2 = "1";
        //s  指向堆空間"1"的記憶體位址
        //s1 指向字元串常量池中"1"的記憶體位址
        //s2 指向字元串常量池已存在的"1"的記憶體位址  是以 s1==s2
        System.out.println(s == s2);//jdk6:false   jdk7/8:false
        System.out.println(s1 == s2);//jdk6: true   jdk7/8:true
        System.out.println(System.identityHashCode(s));//21685669
        System.out.println(System.identityHashCode(s1));//2133927002
        System.out.println(System.identityHashCode(s2));//2133927002

        //s3變量記錄的位址為:new String("11")
        String s3 = new String("1") + new String("1");
        //執行完上一行代碼以後,字元串常量池中,是否存在"11"呢?答案:不存在!!

        //在字元串常量池中生成"11"。如何了解:jdk6:建立了一個新的對象"11",也就有新的位址。
        //         jdk7:此時常量中并沒有建立"11",而是建立一個指向堆空間中new String("11")的位址
        s3.intern();
        //s4變量記錄的位址:使用的是上一行代碼代碼執行時,在常量池中生成的"11"的位址
        String s4 = "11";
        System.out.println(s3 == s4);//jdk6:false  jdk7/8:true
    }

}
           

//在字元串常量池中生成"11"。如何了解:jdk6:建立了一個新的對象"11",也就有新的位址。

// jdk7:此時常量中并沒有建立"11",而是建立一個指向堆空間中new String(“11”)的位址

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
@Test
    public void test3(){
        String s1 = new String("a");
        s1.intern();
        String s2 = "a";
        System.out.println(s1==s2);  // false
        String s3 = new String("b")+new String("c");
        s3.intern();
        String s4 = "bc";
        System.out.println(s3==s4);  //True

    }
           

在JDK 1.7下,當執行s3.intern();時,因為常量池中沒有“bc”這個字元串,是以會在常量池中生成一個對堆中的“bc”的引用(注意這裡是引用 ,就是這個差別于JDK 1.6的地方。在JDK1.6下是生成原字元串的拷貝),而在進行String str1 = “bc”;字面量指派的時候,常量池中已經存在一個引用,是以直接傳回了該引用,是以s3和s4都指向堆中的同一個字元串,傳回true。

其實這個地方的我的最大疑惑就是:s1.intern() 中常量池中存的是"a"的拷貝,為啥 s3.intern()在常量池中存的就是bc的引用?

5.3 拓展

public class StringIntern1 {
    public static void main(String[] args) {
        //StringIntern.java中練習的拓展:
        String s3 = new String("1") + new String("1");//new String("11")
        //執行完上一行代碼以後,字元串常量池中,是否存在"11"呢?答案:不存在!!
        String s4 = "11";//在字元串常量池中生成對象"11"
        String s5 = s3.intern();
        System.out.println(s3 == s4);//false
        System.out.println(s5 == s4);//true
    }
}

           

5.4 總結String的intern()的使用

jdk1.6中,将這個字元串對象嘗試放入串池。

➢如果字元串常量池中有,則并不會放入。傳回已有的串池中的對象的位址

➢如果沒有,會把此對象複制一份,放入串池,并傳回串池中的對象位址

Jdk1.7起,将這個字元串對象嘗試放入串池。

➢如果字元串常量池中有,則并不會放入。傳回已有的串池中的對象的位址

➢如果沒有,則會把對象的引用位址複制一份,放入串池,并傳回串池中的引用位址

5.5 練習1

public class StringExer1 {
    public static void main(String[] args) {
        //String x = "ab";
        String s = new String("a") + new String("b");//new String("ab")
        //在上一行代碼執行完以後,字元串常量池中并沒有"ab"

        String s2 = s.intern();//jdk6中:在串池中建立一個字元串"ab"
                               //jdk8中:串池中沒有建立字元串"ab",而是建立一個引用,指向new String("ab"),将此引用傳回

        System.out.println(s2 == "ab");//jdk6:true  jdk8:true
        System.out.println(s == "ab");//jdk6:false  jdk8:true
    }
}

           

jdk6

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

jdk7/8

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

5.6 練習2

public class StringExer2 {
    public static void main(String[] args) {
        String s1 = new String("ab");//執行完以後,會在字元串常量池中會生成"ab"
//        String s1 = new String("a") + new String("b");執行完以後,不會在字元串常量池中會生成"ab"
        s1.intern();
        String s2 = "ab";
        System.out.println(s1 == s2); //false
    }
}

           

5.7 intern()效率測試

大的網站平台,需要記憶體中存儲大量的字元串。比如社交網站,很多人都存儲:北京市、海澱區等資訊。這時候如果字元串都調用 intern()方法,就會明顯降低記憶體的大小。

/**
 * 使用intern()測試執行效率:空間使用上
 *
 * 結論:對于程式中大量存在存在的字元串,尤其其中存在很多重複字元串時,使用intern()可以節省記憶體空間。
 *
 */
public class StringIntern2 {
    static final int MAX_COUNT = 1000 * 10000;
    static final String[] arr = new String[MAX_COUNT];

    public static void main(String[] args) {
        Integer[] data = new Integer[]{1,2,3,4,5,6,7,8,9,10};

        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX_COUNT; i++) {
//            arr[i] = new String(String.valueOf(data[i % data.length]));
            arr[i] = new String(String.valueOf(data[i % data.length])).intern();

        }
        long end = System.currentTimeMillis();
        System.out.println("花費的時間為:" + (end - start));

        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.gc();
    }
}

           

arr[i] = new String(String.valueOf(data[i % data.length]));

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

arr[i] = new String(String.valueOf(data[i % data.length])).intern();

JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

6.StrtingTable的垃圾回收

/**
 * String的垃圾回收:
 * -Xms15m -Xmx15m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails
 *
 */
public class StringGCTest {
    public static void main(String[] args) {
//        for (int j = 0; j < 100; j++) {
//            String.valueOf(j).intern();
//        }
        //發生垃圾回收行為
        for (int j = 0; j < 100000; j++) {
            String.valueOf(j).intern();
        }
    }
}
           
JVM_09 字元串常量池StringTable1.String的基本特性2.String的記憶體配置設定3.String的基本操作4.字元串拼接操作5.intern()的使用6.StrtingTable的垃圾回收7.G1中的String去重操作

7.G1中的String去重操作

背景:對許多Java應用(有大的也有小的)做的測試得出以下結果:

➢堆存活資料集合裡面String對象占了25%

➢堆存活資料集合裡面重複的String對象有13.5%

➢String對象的平均長度是45

許多大規模的Java應用的瓶頸在于記憶體,測試表明,在這些類型的應用裡面,Java堆中存活的資料集合差不多258是String對象。更進一一步,這裡面差不多一半String對象是重複的,重複的意思是說:string1. equals (string2)=true。堆上存在重複的string對象必然是一種記憶體的浪費。這個項目将在G1垃圾收集器中實作自動持續對重複的String對象進行去重,這樣就能避免浪費記憶體。

實作

➢當垃圾收集器工作的時候,會通路堆上存活的對象。對每一個通路的對象都會檢查是否是候選的要去重的String對象。

➢如果是,把這個對象的一個引用插入到隊列中等待後續的處理。一個去重的線程在背景運作,處理這個隊列。處理隊列的一個元素意味着從隊列删除這個元素,然後嘗試去重它引用的String對象。

➢使用一個hashtable來記錄所有的被String對象使用的不重複的char數組。

當去重的時候,會查這個hashtable,來看堆上是否已經存在一個一模一樣的char數組。

➢如果存在,String對象會被調整引用那個數組,釋放對原來的數組的引用,最終會被垃圾收集器回收掉。

➢如果查找失敗,char數組會被插入到hashtable,這樣以後的時候就可以共享這個數組了。

指令行選項

➢UseStringDeduplication (bool) :開啟String去重,預設是不開啟的,需要手動開啟。

➢PrintStringDedupl icationStatistics (bool) :列印詳細的去重統計資訊,

➢StringDedupl icationAgeThreshold (uintx) :達到這個年齡的string對象被認.為是去重的候選對象