JVM消除重複自負引數-XX:+UseStringDeduplication的優缺點 - JAXenter

banq發表於2019-05-15

什麼是重複的字串?25%的Java應用程式記憶體中填充了字串,其中13.5%是重複的字串。在本文中,Ram Lakshmanan討論了為什麼有這麼多重複的字串,常見的模式是什麼,以及如何處理它。

讓我從一篇有趣的統計資料開始本文(基於JDK開發團隊的研究):

+ 25%的Java應用程式記憶體中充滿了字串。
+ 13.5%是Java應用程式中的重複字串。
+ 平均字串長度為45個字元。


您可以使用HeapHero之類的工具,它可以報告由於重複字串和其他低效程式設計實踐而浪費了多少記憶體。

什麼是重複的字串?
首先,讓我們瞭解重複字串的含義。請看下面的程式碼片段:

String string1 = new String("Hello World");
String string2 = new String("Hello World");


在上面的程式碼中有兩個字串物件string1和string2它們具有相同的內容,即“Hello World”,但它們儲存在兩個不同的物件中。當你執行string1.equals(string2)時,它將返回'true',但'string1 == string2'將返回'false'。這就是我們所說的重複字串。

為什麼有這麼多重複的字串?
應用程式最終會出現大量重複字串的原因有多種。在本節中,我們將回顧兩種最常見的模式:

1. 開發人員為每個請求建立新的字串物件,而不是引用/重用“public static final string literal”。上面的示例可以使用字串文字模式進行最佳編寫:

public static final String HELLO_WORLD = "Hello World";
String string1 = HELLO_WORLD;
String string2 = HELLO_WORLD;


2.假設您正在構建銀行/電子商務應用程式。您正在為資料庫中的每個交易記錄儲存貨幣(即“USD”,“EUR”,“INR”,......)。現在說客戶登入您的應用程式,他正在檢視他的交易歷史頁面。現在,您的應用程式將最終從資料庫中讀取與此客戶相關的所有事務。假設這個客戶住在美國(那麼大部分(如果不是全部)他的交易將以美元為單位)。由於每個交易記錄都有貨幣,因此您的應用程式最終會為從資料庫讀取的每個交易記錄建立一個“USD”字串物件。如果這個客戶有成千上萬的交易,你最終會在記憶體中建立數千個重複的'USD'字串物件,這對於這一個客戶來說也是如此。

同樣,您的應用程式可能會多次從資料庫中讀取多個列(客戶名稱,地址,州,國家/地區,帳號,ID,......)。它們之間可能存在重複。您的應用程式使用外部應用程式讀取和寫入XML / JSON,它操縱了很多字串。所有這些操作都可以/將建立重複的字串。

自從JDK團隊組建開始(20世紀90年代中期)以來,這個問題一直被人們認可,因此到目前為止已經提出了多種解決方案。此解決方案列表的最新成員是'-XX:+ UseStringDeduplication'

-XX:+UseStringDeduplication
嘗試消除重複字串的最小努力是傳遞'-XX:+ UseStringDeduplication'JVM引數。在應用程式啟動期間傳遞此JVM引數時,JVM將嘗試在垃圾收集過程中消除重複的字串。在垃圾收集過程中,JVM會檢查記憶體中的所有物件,因此作為該過程的一部分,它會嘗試識別它們中的重複字串並嘗試消除它。

是否意味著如果你只是傳遞'-XX:+ UseStringDeduplication'JVM引數你能立即節省13.5%的記憶體嗎?:-)聽起來很容易,對吧?我們希望這很容易。但是這個'-XX:+ UseStringDeduplication'解決方案有一些問題。我們來討論一下:

1.僅適用於G1 GC演算法
有幾種垃圾收集演算法(序列,並行,CMS,G1,......)。'-XX:+ UseStringDeduplication'僅在您使用G1 GC演算法時有效。因此,如果您使用其他GC演算法,則需要切換到G1 GC演算法以使用'-XX:+ UseStringDeduplication'。

2.僅適用於長壽命物件
'-XX:+ UseStringDeduplication'消除了較長時間記憶體在的重複字串。它們不會消除短期字串物件中的重複字串。如果物件是短暫的,它們將很快消失,那麼花費資源消除它們中的重複字串的重點是什麼。以下是對主要Java Web應用程式進行的真實案例研究,當使用'-XX:+ UseStringDeduplication'時,該應用程式沒有顯示任何記憶體緩解。但是,如果您的應用程式有很多快取,那麼'-XX:+ UseStringDeduplication'可能是有價值的(因為快取物件通常往往是長期存在的物件)。

3. -XX:StringDeduplicationAgeThreshold
預設情況下,如果字串在3次GC執行中倖存,則符合重複資料刪除的條件。可以透過傳遞'-XX:StringDeduplicationAgeThreshold'來更改3次這個引數。例:
-XX:StringDeduplicationAgeThreshold=6

4.對GC暫停時間的影響
由於在重複垃圾收集期間執行字串重複資料刪除,因此可能會影響GC暫停時間。但是,假設足夠高的重複資料刪除成功率將平衡大部分或全部這種影響,因為重複資料刪除可以減少GC暫停的其他階段所需的工作量(例如減少撤離的物件數量)以及減少GC頻率(由於堆上的壓力減小)。

要分析GC暫停時間影響,您可以考慮使用GCeasy等工具。

5.只替換底層char []
java.lang.String類有兩個欄位:

private final char[] value
private int hash


'-XX:+ UseStringDeduplication'不會消除重複的字串物件本身。它只替換了底層的char []。對String物件進行重複資料刪除在概念上只是對value欄位的重新賦值,即aString.value = anotherString.value。
每個字串物件至少需要24個位元組(字串物件的確切大小取決於JVM配置,但最少24個位元組)。因此,如果存在大量短重複字串,則此功能可以節省更少的記憶體。

6. Java 8 update 20
'-XX:+ UseStringDeduplication'功能僅受Java 8 update 20中支援。因此,如果您在任何舊版本的Java上執行,您將無法使用此功能。

7. -XX:+ PrintStringDeduplicationStatistics
如果您希望檢視字串重複資料刪除統計資訊,例如執行所花費的時間,撤出的重複字串數量,您獲得了多少節省,您可以傳遞'-XX:+ PrintStringDeduplicationStatistics'JVM引數。在錯誤控制檯中將列印統計資訊。

結論
如果您的應用程式使用G1 GC並執行在Java 8更新20之上的版本,您可以考慮啟用'-XX:+ UseStringDeduplication'。您可能會獲得富有成效的結果,尤其是在長壽命物件中存在大量重複字串的情況下。但是,在生產環境中啟用此引數之前,請進行全面測試。

 

相關文章