如何馴服java GC導致暫停? 使用16GiB以上heap

banq發表於2011-06-29
How to tame java GC pauses? Surviving 16GiB heap and greater. | Javalobby

大意如下:
記憶體(條)是便宜,很不幸,垃圾回收機制導致的暫停會嚴重影響系統效能,好像JVM記憶體最多支援2G,作者花費大量時間精力使用32G來提高系統效能,這篇是其心得。

GC微調是非常和應用有關,該篇的目標要求是:使用10G更大Heaps和嚴格的響應時間(毫秒級別),(吞吐量和延遲性是一對矛盾,這次GC微調主要是追求低延遲)。作者專案特點是:

1.Heap用於在記憶體中儲存資料結構
2.Heap大小超過10G
3.請求時間要求更快
4.事務是短的(幾百毫秒) 一個事務可以包括幾個請求
5.記憶體中資料修改頻率和麵積低,不會一秒內修改整個10G記憶體,每秒更新10M即可。

此處省略垃圾回收機制原理介紹.....

總體來說,banq注:JVM分新生代和舊生代,新建立在新生代,透過新生代垃圾回收,如果不能被回收,將逐步轉入舊生代,舊生代記憶體可以實現快取In-memeory資料,顯然新舊兩代的垃圾回收演算法最好不一樣,新生代需要頻繁,而舊生代不需要頻繁,如果我們記憶體快取控制得好,舊生代就不會啟動垃圾回收機制,這樣就不會導致系統暫停。

該文介紹如何來進行新生代大小調節,防止垃圾回收機制頻繁啟動,先使用XX:MaxNewSize=<n> -XX:NewSize=<n> 指定新生代大小,使用-XX:+PrintGCDetails -XX:+PrintTenuringDistribution -XX:+PrintGCTimestamps)輸出GC診斷結果。

有幾個資料需要計算一下:
新生代收集期間值,整個分配情況,短生命物件的分配大小,長生命物件的分配情況,中等生命物件分配。

我個人更喜歡使用psi-Probe安裝在tomcat下,一目瞭然,如附件圖:

原文還提到CMS(Concurrent Mark Sweep)啟用會導致暫停等等。

針對不同應用特點進行演算法配置,線上事務處理將耗費響應時間,而批處理將產生大量垃圾,這些都要用不同JVM來處理。

配置恰當的快取Cache策略來解決GC問題也相當重要,我們提倡基於記憶體的領域物件程式設計,大量資料都在快取中in-memory,如何保證他們的生命週期和JVM的垃圾回收機制不衝突,也是一種實際問題,文章提出下面幾點:
1.為快取增加失效期,讓新生代的GC期間要長於快取的失效期,也就是快取中物件資料失效後,正好GC啟動後可以回收這些失效物件。

2.增加延長新生代的GC期間。

3.在快取中使用弱引用。快取產品自己的事,和我們應用者無關。

4.增加快取大小,我個人經驗儘可能將資料庫資料以領域物件方式都載入到記憶體中。

JVM首先分配所有對在新生代,然後將倖存物件(一般是快取中物件)再分配到舊生代,或再分配到新生代,這些物件的複製將耗費時間導致新生代收集暫停,兩種選擇:
1.在第一次收集時,就將物件複製到舊生代
2.第二次收集時,將物件複製到舊生代。

第一種辦法將導致一些短生命週期物件複製到舊生代,第二種辦法需要注意新生代垃圾收集的頻度和長生命週期物件是否小。

對於一個巨大的heap,新生代垃圾收集暫停主要是在空間掃描和複製倖存物件上,在新生代大小上需要取得一個平衡,舊生代大小足夠放入應用的資料,比如jivejdon一天下來快取的大小是200K左右(每天啟動的情況下),只要舊生代大小大於這個值即可,不能太大,也不能太小。

增加新生代大小要增加整個JVM大小,否則就會降低舊生代的記憶體大小。

可以配置CMS短期暫停:–XX:CMSWaitDuration –設定收集暫停的最大間隔時間,越大你的應用將被暫停,類似當機沒有反應。XX:+CMSScavengeBeforeRemark 可以避免在收集期間同時掃描。

[該貼被banq於2011-06-29 09:59修改過]

相關文章