StringTable結構以及基本調優

今天你做題了嗎發表於2020-07-15

  JDK1.8中StringTable的底層類似於HashTable,由陣列和連結串列實現,陣列又稱為桶陣列。比如有這樣一段程式碼:

public class Demo4 {
    public static void main(String[] args) {
        int i = 0;
        System.out.println(i);
    }
}

我們設定虛擬機器引數“-Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc“,引數具體的意思是 設定堆記憶體大小為10M,輸出StringTableStatistics資訊,輸出GC細節。執行程式碼控制檯會有相應的輸出,主要看下StringTable部分。預設桶的個數是60013,儲存的字串物件個數為1752,串池中字串常量個數為也是1752,總的佔用空間約為0.6M。上面程式碼只是輸出i,但串池中常量個數為1752,那是因為類名、方法名等這些資料也是以常量的形式存在串池中。

接著稍微改動程式碼:

public class Demo4 {
    public static void main(String[] args) {
        int i = 0;
        // 往串池中新增100個字串
        for (int j = 0; j < 100; j++) {
            String.valueOf(j).intern();
            i++;
        }
        System.out.println(i);
    }
}

這時候我們可以看到串池中字串個數為1871,比預設多了100個。

繼續用上述程式碼做實驗,這會for迴圈次數為10000,看控制檯輸出有什麼變化。

字串個數為7451並不是11751,因為我們設定了堆記憶體大小為10M,存10000個字串物件的話堆記憶體發生了MinorGC,將一些無用的物件回收了。

調優

  • 調優主要是對StringTable大小的設定,如果程式中字串數量非常多,那麼可以將桶個數設定大一些,具體引數為 -XX:StringTableSize = 大小,預設是60013,最小是1009。有這樣一段程式碼:
public class Demo5 {
    public static void main(String[] args) throws IOException {
       try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("linux.words"), "utf-8"))) {
            String line = null;
            long start = System.nanoTime();
            while (true) {
                line = reader.readLine();
                if (line == null) {
                    break;
                }
                line.intern();
            }
            System.out.println("cost:" + (System.nanoTime() - start) / 1000000);
        }
    }
}

  "linux.words"中有480000個字串,讀取檔案然後將字串放入串池,記錄整個過程消耗時間,以ms為單位並且列印資訊。預設桶個數下消耗時間為0.57秒。    

        

  我們設定 -XX:StringTableSize=1009再觀察下消耗時間,變為了9.852秒。所以可根據業務場景來設定桶個數大小。 

        

  • 如果應用裡有大量字串並且存在很多重複的字串,可以考慮使用intern()方法將字串入池,而不是都存在Eden區中,這樣字串僅會佔用較少的空間。

 

相關文章