歡迎關(guān)注公眾號(hào)OpenCoder力九,來和我做朋友吧~??????
今天給大家分享一個(gè)真實(shí)的案例,這是我之前一個(gè)朋友公司的項(xiàng)目在開發(fā)完畢后上線發(fā)現(xiàn)整個(gè)項(xiàng)目在線上的體驗(yàn)非常不好邑闺,有明顯的頻繁卡頓跌前,后來經(jīng)過一連串的排查、定位陡舅、分析和優(yōu)化才發(fā)現(xiàn)原來是一條SQL引發(fā)的問題抵乓,而且該SQL語句是一位剛?cè)肼毑痪玫墓こ處熕鶎憽?/p>
通過這次事故的教訓(xùn)也讓這個(gè)團(tuán)隊(duì)在后續(xù)的開發(fā)中更加注重了JVM參數(shù)的設(shè)置以及GC的監(jiān)控。
該案例肯定涉及到整個(gè)項(xiàng)目的代碼關(guān)聯(lián)蹭沛,無法給到大家具體代碼的展示臂寝,我們本案例還是通過畫圖的方式給大家進(jìn)行講解。對(duì)于該案例的分析和優(yōu)化的過程大家如果能理解透徹摊灭,舉一反三咆贬,對(duì)于大家以后在自己公司的項(xiàng)目以及遇到類似的情況,能夠結(jié)合我們講解的Jvm知識(shí)和掌握的工具自己去進(jìn)行排查和優(yōu)化肯定是有幫助的帚呼。
JVM性能分析-找問題
那么針對(duì)類似這種系統(tǒng)上線后就比較卡頓的問題掏缎,我們應(yīng)該如何下手去進(jìn)行排查呢?
我們之前已經(jīng)介紹過 jstat工具了煤杀,這個(gè)工具是非常好用眷蜈、實(shí)用的一個(gè)工具,希望大家重視起來沈自。
由于很多中小型公司其實(shí)都沒有可視化的監(jiān)控平臺(tái)酌儒,沒法直接可視化的看到JVM各個(gè)區(qū)域的內(nèi)存變化,GC次數(shù)和GC耗時(shí)枯途。
當(dāng)然忌怎,如果有辦法的話籍滴,我建議大家可以給自己所在公司推薦一下類似Zabbix、Ganglia榴啸、Open-Falcon孽惰、Prometheus之類的可視化監(jiān)控平臺(tái),其實(shí)接入都非常簡(jiǎn)單鸥印,如果把線上系統(tǒng)接入了這些平臺(tái)勋功,可以直接圖形化看到JVM的表現(xiàn)。
但是哪怕你有了可視化監(jiān)控平臺(tái)库说,有時(shí)候直接對(duì)線上系統(tǒng)進(jìn)行分析的時(shí)候狂鞋,還是jstat更加好用和直接。
OK潜的,具體如何通過 jstat工具進(jìn)行數(shù)據(jù)分析和打印要销,請(qǐng)查看或回顧:28-虛擬機(jī)性能監(jiān)控&故障處理工具
jstat排查后的數(shù)據(jù)統(tǒng)計(jì)
- 機(jī)器配置:2核4G
- JVM堆內(nèi)存大小:2G
- 系統(tǒng)運(yùn)行時(shí)間:3天
- 系統(tǒng)運(yùn)行3天內(nèi)發(fā)生的Full GC次數(shù)和耗時(shí):125次夏块,30多秒
- 系統(tǒng)運(yùn)行3天內(nèi)發(fā)生的Young GC次數(shù)和耗時(shí):1.3萬次疏咐,700秒
該系統(tǒng)通過jstat工具監(jiān)測(cè)三天后得到的數(shù)據(jù)統(tǒng)計(jì)如上,我們發(fā)現(xiàn)基本上每天就會(huì)發(fā)生40多次Full GC脐供,每一個(gè)小時(shí)大概2次Full GC浑塞,每次Full GC耗時(shí)300ms左右; 每天發(fā)生4000多次Yong GC政己,基本每分鐘就發(fā)生3次酌壕,每次GC 50ms左右。
根據(jù)以上數(shù)據(jù)分析我們可以看出這個(gè)系統(tǒng)的性能是相當(dāng)?shù)牟盍诵桑还苁荵ong GC還是Full GC的頻率太頻繁了卵牍!必須進(jìn)行優(yōu)化。
未優(yōu)化前的線上JVM參數(shù)
下面是未優(yōu)化前的線上JVM參數(shù)沦泌,大致如下:
-Xms1536M -Xmx1536M -Xmn512M -Xss256K
-XX:SurvivorRatio=5 -XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=68
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
可以看到糊昙,4G的機(jī)器,給JVM內(nèi)存分配了1.5G谢谦,新生代分配了512M释牺,老年代1G。
這里關(guān)鍵的是“-XX:SurvivorRatio”設(shè)置為了5回挽,也就是說没咙,Eden:Survivor1:Survivor2的比例是5:1:1。
那么可以推出:Eden區(qū)域大致為 366M千劈,每個(gè)Survior區(qū)域大致為 70M祭刚。
如下圖所示:
系統(tǒng)運(yùn)行推導(dǎo)
1.新生代每秒分配對(duì)象的大小
由之前的統(tǒng)計(jì)分析可知:每分鐘3次Yong GC,那么大概20s就會(huì)發(fā)生一次Yong GC,那么也就是20秒Eden區(qū)域基本就滿了涡驮,20S共產(chǎn)生300多MB的對(duì)象宜鸯,每秒大概產(chǎn)生 20MB的對(duì)象進(jìn)Eden.
2.老年代分配對(duì)象時(shí)間
每一個(gè)小時(shí)大概2次Full GC,那么半小時(shí)就是一個(gè)Full GC遮怜,要想觸發(fā)Full GC,需要達(dá)到老年代觸發(fā)的閾值鸿市,而上述JVM參數(shù)中有一個(gè)
-XX:CMSInitiatingOccupancyFraction=68
參數(shù)的設(shè)置锯梁,那么也就是老年代空間大概在達(dá)到600多MB的時(shí)候就直接觸發(fā)Full GC了。
總結(jié)下如下圖所示:
那么分析到這兒焰情,可能有多同學(xué)就會(huì)立馬給出解決方案了:
- 換機(jī)器:換成4核8G
- 增大Survior區(qū)域的大小
請(qǐng)注意:并不是所有的問題都是可以通過增大機(jī)器內(nèi)存陌凳,增大JVM內(nèi)存,增大新生代內(nèi)存内舟,增大Survior區(qū)域大小就可以解決了合敦,我們一定要先分析清楚到底是什么原因?qū)е挛覀兊南到y(tǒng)會(huì)發(fā)生卡頓,是不是由于我們自己的代碼問題所導(dǎo)致验游? 特別是在機(jī)器內(nèi)存小的物理機(jī)上更容易檢測(cè)出來我們代碼的質(zhì)量問題充岛,這類問題如果不解決,而是一味的增大機(jī)器內(nèi)存耕蝉,根本是解決不了本質(zhì)問題的崔梗!
這里真正的Full GC是真的由于Survior區(qū)域太小,導(dǎo)致每次Yong GC后剩余存活對(duì)象直接進(jìn)入到了老年代垒在?
還是有很多長(zhǎng)時(shí)間存活對(duì)象蒜魄,始終存活在老年代,導(dǎo)致無法回收场躯?加上老年代觸發(fā)Full GC的閾值只有68%谈为,導(dǎo)致速度更快?
分析到這里踢关,絕不能輕易草率的給出結(jié)論和解決方案伞鲫!
那么我們到底應(yīng)該如何繼續(xù)分析以及排查問題,請(qǐng)大家積極思考可以在我們的技術(shù)交流群中給出自己的答案和分析計(jì)劃签舞。我們下篇文章再繼續(xù)給出問題所在和解決方案榔昔。