JVM內存設置多大合適?Xmx和Xmn如何設置?

轉載地址 https://www.dutycode.com/jvm_xmx_xmn_xms_shezhi.html

問題:

新上線一個java服務,或者是RPC或者是WEB站點, 內存的設置該怎麼設置呢?設置成多大比較合適,既不浪費內存,又不影響性能呢?

分析:

依據的原則是根據Java Performance裏面的推薦公式來進行設置。

具體來講:

Java整個堆大小設置,Xmx 和 Xms設置爲老年代存活對象的3-4倍,即FullGC之後的老年代內存佔用的3-4倍

永久代 PermSize和MaxPermSize設置爲老年代存活對象的1.2-1.5倍。

年輕代Xmn的設置爲老年代存活對象的1-1.5倍。

老年代的內存大小設置爲老年代存活對象的2-3倍。

BTW:

     1、Sun官方建議年輕代的大小爲整個堆的3/8左右, 所以按照上述設置的方式,基本符合Sun的建議。 

     2、堆大小=年輕代大小+年老代大小, 即xmx=xmn+老年代大小 。 Permsize不影響堆大小。

     3、爲什麼要按照上面的來進行設置呢? 沒有具體的說明,但應該是根據多種調優之後得出的一個結論。

如何確認老年代存活對象大小?

方式1(推薦/比較穩妥):

     JVM參數中添加GC日誌,GC日誌中會記錄每次FullGC之後各代的內存大小,觀察老年代GC之後的空間大小。可觀察一段時間內(比如2天)的FullGC之後的內存情況,根據多次的FullGC之後的老年代的空間大小數據來預估FullGC之後老年代的存活對象大小(可根據多次FullGC之後的內存大小取平均值)

方式2:(強制觸發FullGC, 會影響線上服務,慎用)

     方式1的方式比較可行,但需要更改JVM參數,並分析日誌。同時,在使用CMS回收器的時候,有可能不能觸發FullGC(只發生CMS GC),所以日誌中並沒有記錄FullGC的日誌。在分析的時候就比較難處理。

     BTW:使用jstat -gcutil工具來看FullGC的時候, CMS GC是會造成2次的FullGC次數增加。 具體可參見之前寫的一篇關於jstat使用的文章

     所以,有時候需要強制觸發一次FullGC,來觀察FullGC之後的老年代存活對象大小。

     注:強制觸發FullGC,會造成線上服務停頓(STW),要謹慎,建議的操作方式爲,在強制FullGC前先把服務節點摘除,FullGC之後再將服務掛回可用節點,對外提供服務

     在不同時間段觸發FullGC,根據多次FullGC之後的老年代內存情況來預估FullGC之後的老年代存活對象大小

如何觸發FullGC ?

       使用jmap工具可觸發FullGC 

        jmap -dump:live,format=b,file=heap.bin <pid> 將當前的存活對象dump到文件,此時會觸發FullGC

        jmap -histo:live <pid> 打印每個class的實例數目,內存佔用,類全名信息.live子參數加上後,只統計活的對象數量. 此時會觸發FullGC

具體操作實例:

以我司的一個RPC服務爲例。

BTW:剛上線的新服務,不知道該設置多大的內存的時候,可以先多設置一點內存,然後根據GC之後的情況來進行分析。

初始JVM內存參數設置爲: Xmx=2G Xms=2G xmn=1G 

使用jstat 查看當前的GC情況。如下圖:

YGC平均耗時: 173.825s/15799=11ms

FGC平均耗時:0.817s/41=19.9ms

平均大約10-20s會產生一次YGC

看起來似乎不錯,YGC觸發的頻率不高,FGC的耗時也不高,但這樣的內存設置是不是有些浪費呢?

爲了快速看數據,我們使用了方式2,產生了幾次FullGC,FullGC之後,使用的jmap -heap 來看的當前的堆內存情況(也可以根據GC日誌來看)

heap情況如下圖:(命令 : jmap -heap <pid>)

上圖中的concurrent mark-sweep generation即爲老年代的內存描述。

老年代的內存佔用爲100M左右。 按照整個堆大小是老年代(FullGC)之後的3-4倍計算的話,設置各代的內存情況如下:

     Xmx=512m  Xms=512m  Xmn=128m PermSize=128m  老年代的大小爲 (512-128=384m)爲老年代存活對象大小的3倍左右

調整之後的,heap情況

GC情況如下:

YGC 差不多在10s左右觸發一次。每次YGC平均耗時大約9.41ms。可接受。

FGC平均耗時:0.016s/2=8ms

整體的GC耗時減少。但GC頻率比之前的2G時的要多了一些。

注: 看上述GC的時候,發現YGC的次數突然會增多很多個,比如 從1359次到了1364次。具體原因是?

總結:

在內存相對緊張的情況下,可以按照上述的方式來進行內存的調優, 找到一個在GC頻率和GC耗時上都可接受的一個內存設置,可以用較小的內存滿足當前的服務需要

但當內存相對寬裕的時候,可以相對給服務多增加一點內存,可以減少GC的頻率,GC的耗時相應會增加一些。 一般要求低延時的可以考慮多設置一點內存, 對延時要求不高的,可以按照上述方式設置較小內存。 

補充:

永久代(方法區)並不在堆內,所以之前有看過一篇文章中描述的 整個堆大小=年輕代+年老代+永久代的描述是不正確的。

-verbose:gc 現實垃圾收集信息

-Xloggc:gc.log 指定垃圾收集日誌文件

-Xmn:young generation的heap大小,一般設置爲Xmx的3、4分之一

-XX:SurvivorRatio=2  :生還者池的大小,默認是2,如果垃圾回收變成了瓶頸,您可以嘗試定製生成池設置

-XX:NewSize: 新生成的池的初始大小。 缺省值爲2M。

-XX:MaxNewSize: 新生成的池的最大大小。  缺省值爲32M。

+XX:AggressiveHeap 會使得 Xms沒有意義。這個參數讓jvm忽略Xmx參數,瘋狂地吃完一個G物理內存,再吃盡一個G的swap。

-Xss:每個線程的Stack大小,“-Xss 15120” 這使得JBoss每增加一個線程(thread)就會立即消耗15M內存,而最佳值應該是128K,默認值好像是512k.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章