Source的內(nèi)存運(yùn)行情況
Source作為公司內(nèi)部代碼托管工具,用戶通過git的push、pull、clone等操作以及在web端查看代碼進(jìn)行代碼對比的操作都將在短時(shí)間內(nèi)產(chǎn)生大量的對象,并且這些對象的存活時(shí)間也不會(huì)很長。
調(diào)優(yōu)之前的jvm運(yùn)行情況分析
調(diào)優(yōu)之前jvm使用的垃圾回收策略是新生代老年代均使用parallel Scanvenge,也就是默認(rèn)策略,堆大小為2G。出現(xiàn)的問題是:
- 永久代初始內(nèi)存64M設(shè)置過小,運(yùn)行過程中永久代內(nèi)存可達(dá)到90M
- Minor gc頻繁,說明eden區(qū)內(nèi)存不足
- Minor gc時(shí)間過長,平均一次需要80ms
- 垃圾收集的并行線程數(shù)43個(gè),服務(wù)器是8核,線程數(shù)偏多
- Full gc頻繁,平均是半個(gè)小時(shí)一次,一次full gc耗時(shí)800ms左右
第一次調(diào)優(yōu)
參數(shù)設(shè)置:
-XX:+PrintGCDetails
-XX:+DisableExplicitGC
-XX:+UseParNewGC
-XX:ParallelGCThreads=20
-Xmn900m
-XX:SurvivorRatio=1
-XX:PermSize=128m
-Xmx2048m
-Xms2048m
第一次調(diào)優(yōu)新生代收集器改用parnew,parnew收集器不會(huì)動(dòng)態(tài)的去調(diào)整新生代大小去達(dá)到吞吐率要求,但更容易控制。垃圾收集線程數(shù)改為20,減少線程之間的切換開銷,提升minor gc速度,設(shè)置survivor:eden=1:1,永久代初始大小設(shè)置為128m,老年代收集器默認(rèn)是單線程的serial old。同時(shí)禁止代碼顯式GC。
得到的結(jié)果是full頻率下降很多,這里的原因在于survivor區(qū)增大,因?yàn)閟urvivor區(qū)內(nèi)存不足而直接進(jìn)入老年代的情況減少,minor gc頻率有所提高,minor gc耗費(fèi)的平均時(shí)間變?yōu)榇饲暗囊话耄l率更高的原因可能在于eden區(qū)300m大小偏小,而耗費(fèi)時(shí)間的減少一方面是eden區(qū)較小,另一方面則是垃圾收集線程的切換開銷減小了。
第二次調(diào)優(yōu)
參數(shù)設(shè)置:
-XX:+PrintGCDetails
-XX:+DisableExplicitGC
-XX:+UseParNewGC
-XX:ParallelGCThreads=20
-Xmn900m
-XX:SurvivorRatio=2
-XX:PermSize=128m
-XX:MaxTenuringThreshold=10
-Xmx2048m
-Xms2048m
第二次調(diào)優(yōu)修改eden區(qū)大小為450m,力圖降低minor gc頻率,同時(shí)設(shè)置經(jīng)過多少次minor gc過后,存活的對象進(jìn)入老年代,默認(rèn)值15,調(diào)整過后full gc的頻率有所提高,minor gc頻率仍然偏高,說明eden區(qū)還是太小,此外過小的survivor區(qū)大小也影響了full gc的頻率。
第三次調(diào)優(yōu)
參數(shù)設(shè)置:
-XX:+PrintGCDetails
-XX:+DisableExplicitGC
-XX:+UseParNewGC
-XX:ParallelGCThreads=8
-Xmn1000m
-XX:SurvivorRatio=2
-XX:PermSize=128m
-XX:MaxTenuringThreshold=10
-Xmx2048m
-Xms2048m
第三次增大年輕代大小到1000m,減少gc時(shí)的并發(fā)線程數(shù)至CPU核數(shù)相同,結(jié)果是minor gc頻率降低,并且每次minor gc時(shí)間驟降到16ms左右,十個(gè)小時(shí)內(nèi)進(jìn)行過一次full gc,耗時(shí)接近1s。
繼續(xù)觀察出現(xiàn)的問題,由于短暫的出現(xiàn)大量的大對象,造成短時(shí)間內(nèi)大量的minor gc并且由此引發(fā)了數(shù)次full gc,判斷是否是一次“良好”的minor gc可以從本次gc時(shí)新生代大小判斷,如果大小接近最大值說明引發(fā)gc的對象不大,如果當(dāng)前新生代大小很小,說明出現(xiàn)了大對象,并且該對象很有可能將會(huì)由于survivor不夠大而進(jìn)入老年代。
結(jié)合三次調(diào)優(yōu)可以發(fā)現(xiàn)source jvm的內(nèi)存空間還不太夠。
第四次調(diào)優(yōu)
參數(shù)設(shè)置:
-XX:+PrintGCDateStamps
-XX:TargetSurvivorRatio=100
-XX:+PrintTenuringDistribution //minor gc后打印分代信息
-XX:+UseConcMarkSweepGC
-XX:+CMSParallelRemarkEnabled //啟用CMS并發(fā)標(biāo)記
-XX:+UseCMSCompactAtFullCollection //啟用碎片整理
-XX:CMSFullGCsBeforeCompaction=0 //設(shè)置多少次FULL GC過后執(zhí)行一次碎片整理
-XX:+UseCMSInitiatingOccupancyOnly //啟用CMS占比閾值
-XX:CMSInitiatingOccupancyFraction=80 //設(shè)置閾值百分比,內(nèi)存超過該百分比將執(zhí)行CMS GC
-XX:SurvivorRatio=2
-XX:+PrintGCDetails
-XX:+UseParNewGC
-XX:ParallelGCThreads=8
-Xmn1500m
-XX:+DisableExplicitGC
-XX:PermSize=128m
-XX:MaxTenuringThreshold=10
-Xmx4096m
-Xms4096m
這一次調(diào)優(yōu)是經(jīng)過了幾個(gè)參數(shù)調(diào)整的最終成型結(jié)果,有如下一些比較重要的改變
- 老年代收集器使用CMS,CMS分為多個(gè)階段,其中耗時(shí)的階段都不是stop the world,因此可以顯著提高系統(tǒng)響應(yīng)性能,缺點(diǎn)是采用標(biāo)記清除算法,因此存在內(nèi)存碎片問題以及并發(fā)模式下的失敗問題。內(nèi)存碎片問題可以通過設(shè)置full gc后執(zhí)行內(nèi)存整理來解決,這里必須要注意的是CMS GC不是FULL GC,F(xiàn)ULL GC停頓的時(shí)間會(huì)很長,理想情況下如果FULL GC頻率超過一天一次,可以考慮在外部通過腳本在凌晨執(zhí)行一次定時(shí)FULL GC以達(dá)到清理碎片的效果。并發(fā)模式下的失敗問題一個(gè)顯著的原因是垃圾收集的速度已經(jīng)跟不上對象分配的速度, source針對大代碼庫的服務(wù)器會(huì)偶發(fā)這個(gè)失敗問題。
- -XX:TargetSurvivorRatio=100強(qiáng)調(diào)下這個(gè)參數(shù),該參數(shù)表示survivor區(qū)的使用率,如果survivor去的對象大小超過了使用率則會(huì)以survivor區(qū)age的最大值作為晉升老年代的參考,最終取該參考值以及設(shè)置的最大晉升代數(shù)的較小值,hot spot默認(rèn)是50,這里改成100,意思就是對象必須待滿足夠的代數(shù)才能進(jìn)入老年代,結(jié)合-XX:MaxTenuringThreshold=10可知對象需要待滿10代。為什么選擇10代,這是通過設(shè)置-XX:+PrintTenuringDistribution觀察存活對象晉升的情況作出的決定(這里并沒有經(jīng)過絕對準(zhǔn)確的調(diào)研,要想準(zhǔn)確的得到更合理的分代門限值可以做一個(gè)晉升的統(tǒng)計(jì)),實(shí)際當(dāng)中發(fā)現(xiàn)大部分對象無法存活超過10代。
- CMS GC啟動(dòng)閾值-XX:CMSInitiatingOccupancyFraction=80,這個(gè)值非常重要,實(shí)際情況下可以根據(jù)jvm具體情形設(shè)置。這里設(shè)置為80表示一旦老年代內(nèi)存空間超過百分之八十將進(jìn)行CMS GC。預(yù)留的空間可以保證在CMS GC的并發(fā)階段新進(jìn)入老年代的對象可以找到空間進(jìn)行存儲(chǔ),一旦找不到足夠的空間就將產(chǎn)生并發(fā)模式錯(cuò)誤并觸發(fā)FULL GC。
調(diào)優(yōu)之后新生代GC頻率下降,時(shí)間有所提升,變?yōu)?0+ms(完全可以接受),老年代GC頻率驟降,平均一天一次(非大代碼庫),高峰期可能一天會(huì)出現(xiàn)2-3次,XX:TargetSurvivorRatio=100大大地降低了新生代對象進(jìn)入老年代的可能性,因?yàn)榇蟛糠謱ο蟠婊顣r(shí)間都無法超過10代。
目前source非大代碼庫對應(yīng)的服務(wù)器一直使用該配置,運(yùn)行情況良好,并且比起調(diào)優(yōu)之前性能好了很多。