問題
一個新上線java服務(wù)贮乳, 內(nèi)存的設(shè)置該怎么設(shè)置呢恬惯?設(shè)置成多大比較合適酪耳,既不浪費內(nèi)存,又不影響性能呢颈将?
分析:
依據(jù)的原則是根據(jù)Java Performance里面的推薦公式來進(jìn)行設(shè)置晴圾。
具體來講:
Java整個堆大小設(shè)置死姚,Xmx 和 Xms設(shè)置為老年代存活對象的3-4倍,即FullGC之后的老年代內(nèi)存占用的3-4倍
永久代 PermSize和MaxPermSize設(shè)置為老年代存活對象的1.2-1.5倍”M停【永久區(qū)并不是老年代的1.2到1.5倍,而是FullGC后永久區(qū)的1.2到1.5倍 1.2x to 1.5x permanent generation space】
年輕代Xmn的設(shè)置為老年代存活對象的1-1.5倍竟终。
老年代的內(nèi)存大小設(shè)置為老年代存活對象的2-3倍。
BTW:
1喘鸟、Sun官方建議年輕代的大小為整個堆的3/8左右, 所以按照上述設(shè)置的方式崎淳,基本符合Sun的建議拣凹。
2嚣镜、堆大小=年輕代大小+年老代大小祈惶, 即xmx=xmn+老年代大小 捧请。 Permsize不影響堆大小疹蛉。
3可款、為什么要按照上面的來進(jìn)行設(shè)置呢? 沒有具體的說明闺鲸,但應(yīng)該是根據(jù)多種調(diào)優(yōu)之后得出的一個結(jié)論。
如何確認(rèn)老年代存活對象大辛⑾狻?
方式1(推薦/比較穩(wěn)妥):
JVM參數(shù)中添加GC日志类早,GC日志中會記錄每次FullGC之后各代的內(nèi)存大小缭召,觀察老年代GC之后的空間大小嵌巷。可觀察一段時間內(nèi)(比如2天)的FullGC之后的內(nèi)存情況蛙卤,根據(jù)多次的FullGC之后的老年代的空間大小數(shù)據(jù)來預(yù)估FullGC之后老年代的存活對象大小(可根據(jù)多次FullGC之后的內(nèi)存大小取平均值)
方式2:(強(qiáng)制觸發(fā)FullGC, 會影響線上服務(wù)已维,慎用)
方式1的方式比較可行行嗤,但需要更改JVM參數(shù),并分析日志垛耳。同時栅屏,在使用CMS回收器的時候,有可能不能觸發(fā)FullGC(只發(fā)生CMS GC)堂鲜,所以日志中并沒有記錄FullGC的日志栈雳。在分析的時候就比較難處理。
BTW:使用jstat -gcutil工具來看FullGC的時候缔莲, CMS GC是會造成2次的FullGC次數(shù)增加哥纫。 具體可參見之前寫的一篇關(guān)于jstat使用的文章
所以,有時候需要強(qiáng)制觸發(fā)一次FullGC痴奏,來觀察FullGC之后的老年代存活對象大小蛀骇。
注:強(qiáng)制觸發(fā)FullGC暑诸,會造成線上服務(wù)停頓(STW)伞矩,要謹(jǐn)慎,建議的操作方式為,在強(qiáng)制FullGC前先把服務(wù)節(jié)點摘除,F(xiàn)ullGC之后再將服務(wù)掛回可用節(jié)點,對外提供服務(wù)
在不同時間段觸發(fā)FullGC,根據(jù)多次FullGC之后的老年代內(nèi)存情況來預(yù)估FullGC之后的老年代存活對象大小
如何觸發(fā)FullGC ?
使用jmap工具可觸發(fā)FullGC
jmap -dump:live,format=b,file=heap.bin <pid> 將當(dāng)前的存活對象dump到文件拾氓,此時會觸發(fā)FullGC
jmap -histo:live <pid> 打印每個class的實例數(shù)目,內(nèi)存占用,類全名信息.live子參數(shù)加上后,只統(tǒng)計活的對象數(shù)量. 此時會觸發(fā)FullGC
具體操作實例:
以我司的一個RPC服務(wù)為例奶陈。
BTW:剛上線的新服務(wù)徐勃,不知道該設(shè)置多大的內(nèi)存的時候,可以先多設(shè)置一點內(nèi)存冀自,然后根據(jù)GC之后的情況來進(jìn)行分析驻呐。
初始JVM內(nèi)存參數(shù)設(shè)置為: Xmx=2G Xms=2G xmn=1G
使用jstat 查看當(dāng)前的GC情況绣张。如下圖:
YGC平均耗時: 173.825s/15799=11ms
FGC平均耗時:0.817s/41=19.9ms
平均大約10-20s會產(chǎn)生一次YGC
看起來似乎不錯,YGC觸發(fā)的頻率不高,F(xiàn)GC的耗時也不高奔浅,但這樣的內(nèi)存設(shè)置是不是有些浪費呢?
為了快速看數(shù)據(jù),我們使用了方式2,產(chǎn)生了幾次FullGC菠红,F(xiàn)ullGC之后遇绞,使用的jmap -heap 來看的當(dāng)前的堆內(nèi)存情況(也可以根據(jù)GC日志來看)
heap情況如下圖:(命令 : jmap -heap <pid>)
上圖中的concurrent mark-sweep generation即為老年代的內(nèi)存描述付鹿。
老年代的內(nèi)存占用為100M左右徽诲。 按照整個堆大小是老年代(FullGC)之后的3-4倍計算的話,設(shè)置各代的內(nèi)存情況如下:
Xmx=512m Xms=512m Xmn=128m PermSize=128m 老年代的大小為 (512-128=384m)為老年代存活對象大小的3倍左右
調(diào)整之后的硫麻,heap情況
GC情況如下:
YGC 差不多在10s左右觸發(fā)一次柳洋。每次YGC平均耗時大約9.41ms】奂祝可接受琉挖。
FGC平均耗時:0.016s/2=8ms
整體的GC耗時減少顽耳。但GC頻率比之前的2G時的要多了一些。
注: 看上述GC的時候,發(fā)現(xiàn)YGC的次數(shù)突然會增多很多個,比如 從1359次到了1364次酸舍。具體原因是?
總結(jié):
在內(nèi)存相對緊張的情況下里初,可以按照上述的方式來進(jìn)行內(nèi)存的調(diào)優(yōu)啃勉, 找到一個在GC頻率和GC耗時上都可接受的一個內(nèi)存設(shè)置,可以用較小的內(nèi)存滿足當(dāng)前的服務(wù)需要
但當(dāng)內(nèi)存相對寬裕的時候双妨,可以相對給服務(wù)多增加一點內(nèi)存淮阐,可以減少GC的頻率,GC的耗時相應(yīng)會增加一些刁品。 一般要求低延時的可以考慮多設(shè)置一點內(nèi)存泣特, 對延時要求不高的,可以按照上述方式設(shè)置較小內(nèi)存挑随。
補(bǔ)充:
永久代(方法區(qū))并不在堆內(nèi)状您,所以之前有看過一篇文章中描述的 整個堆大小=年輕代+年老代+永久代的描述是不正確的。
jmap -heap pid 結(jié)果參數(shù)詳解
Debugger attached successfully.
Server compiler detected.
JVM version is 25.171-b11
using thread-local object allocation.
Parallel GC with 8 thread(s) //采用Parallel GC
Heap Configuration:
MinHeapFreeRatio = 0 //JVM最小空閑比率 可由-XX:MinHeapFreeRatio=<n>參數(shù)設(shè)置兜挨, jvm heap 在使用率小于 n 時 ,heap 進(jìn)行收縮
MaxHeapFreeRatio = 100 //JVM最大空閑比率 可由-XX:MaxHeapFreeRatio=<n>參數(shù)設(shè)置膏孟, jvm heap 在使用率大于 n 時 ,heap 進(jìn)行擴(kuò)張
MaxHeapSize = 2095054848 (1998.0MB) //JVM堆的最大大小 可由-XX:MaxHeapSize=<n>參數(shù)設(shè)置
NewSize = 44040192 (42.0MB) //JVM新生代的默認(rèn)大小 可由-XX:NewSize=<n>參數(shù)設(shè)置
MaxNewSize = 698351616 (666.0MB) //JVM新生代的最大大小 可由-XX:MaxNewSize=<n>參數(shù)設(shè)置
OldSize = 88080384 (84.0MB) //JVM老生代的默認(rèn)大小 可由-XX:OldSize=<n>參數(shù)設(shè)置
NewRatio = 2 //新生代:老生代(的大小)=1:2 可由-XX:NewRatio=<n>參數(shù)指定New Generation與Old Generation heap size的比例拌汇。
SurvivorRatio = 8 //survivor:eden = 1:8,即survivor space是新生代大小的1/(8+2)[因為有兩個survivor區(qū)域] 可由-XX:SurvivorRatio=<n>參數(shù)設(shè)置
MetaspaceSize = 21807104 (20.796875MB) //元空間的默認(rèn)大小柒桑,超過此值就會觸發(fā)Full GC 可由-XX:MetaspaceSize=<n>參數(shù)設(shè)置
CompressedClassSpaceSize = 1073741824 (1024.0MB) //類指針壓縮空間的默認(rèn)大小 可由-XX:CompressedClassSpaceSize=<n>參數(shù)設(shè)置
MaxMetaspaceSize = 17592186044415 MB //元空間的最大大小 可由-XX:MaxMetaspaceSize=<n>參數(shù)設(shè)置
G1HeapRegionSize = 0 (0.0MB) //使用G1垃圾收集器的時候,堆被分割的大小 可由-XX:G1HeapRegionSize=<n>參數(shù)設(shè)置
Heap Usage:
PS Young Generation //新生代區(qū)域分配情況
Eden Space: //Eden區(qū)域分配情況
capacity = 89653248 (85.5MB)
used = 8946488 (8.532035827636719MB)
free = 80706760 (76.96796417236328MB)
9.978989272089729% used
From Space: //其中一個Survivor區(qū)域分配情況
capacity = 42467328 (40.5MB)
used = 15497496 (14.779563903808594MB)
free = 26969832 (25.720436096191406MB)
36.49275037977431% used
To Space: //另一個Survivor區(qū)域分配情況
capacity = 42991616 (41.0MB)
used = 0 (0.0MB)
free = 42991616 (41.0MB)
0.0% used
PS Old Generation //老生代區(qū)域分配情況
capacity = 154664960 (147.5MB)
used = 98556712 (93.99100494384766MB)
free = 56108248 (53.508995056152344MB)
63.722715216167906% used
1819 interned Strings occupying 163384 bytes.```