引言
眾里尋他千百度奶稠,驀然回首陨献,還是垃圾回收盒犹;內(nèi)存占用過高,cpu負(fù)載居高不下眨业,如何高效的借助工具來排查問題急膀,讓我們跟隨本文來抽絲剝繭,讓頭疼的垃圾回收和full gc問題浮出水面...
上周四微信上收到智能告警龄捡,某應(yīng)用出現(xiàn)full gc頻繁問題卓嫂,初步觀察了下sgm和線上機(jī)器情況,定位問題聘殖。每次告警都是對我們每個(gè)金融消防員的考驗(yàn):如履薄冰晨雳,膽戰(zhàn)心驚。
報(bào) 警
立 案 調(diào) 查
分析依據(jù):發(fā)生fullGC的最常見情況是老年代或者永久代空間不足時(shí)奸腺。
現(xiàn)場分析:通過SGM查看老年代和永久代的空間占比剩余空間還有一定比例餐禁,不至于發(fā)生fullGC,發(fā)生原因最后分析突照。
另外sgm上看了下jvm監(jiān)控帮非,發(fā)現(xiàn)堆內(nèi)存從14號上午竄上去后再?zèng)]下來過,下面這個(gè)圖很容易定位是發(fā)生了內(nèi)存泄漏讹蘑,以下的思路就順著定位內(nèi)存泄漏的程序進(jìn)行
- 排查:看了下mapi代碼提交記錄末盔,近期無上線,初步排除新上線代碼問題座慰。
在sure上使用jmap命令陨舱,發(fā)現(xiàn)char占據(jù)大量內(nèi)存,懷疑存在大字符串版仔。
周五找運(yùn)維下了一份詳細(xì)的dump文件隅忿,使用亮哥之前分享過的IBMHeapAnalyzer工具心剥,分析發(fā)現(xiàn)問題可能出在EnterRealNameApplyUploadImgReqModel類里,這個(gè)類是用于實(shí)名申請時(shí)圖片上傳接口的入?yún)?shí)體類背桐,里面包含了圖片的base64的string串优烧,占用較大空間。
排查mapi底層biz系統(tǒng)链峭,查看EnterRealNameApplyUploadImgReqModel對應(yīng)的實(shí)現(xiàn)類畦娄,發(fā)現(xiàn)biz中有對圖片大小進(jìn)行限制,最大為2M弊仪,但是mapi無限制熙卡,懷疑可能為此接口中上傳圖片過大。
經(jīng)磊哥點(diǎn)撥励饵,發(fā)現(xiàn)sdk中對base串做了加密驳癌,并在mapi中做了解密處理,加解密工具為靜態(tài)(static)工具方法役听,可能導(dǎo)致內(nèi)存泄漏颓鲜。
定位到問題后,再使用亮哥推薦的visualVM插件典予,在本地啟了mapi應(yīng)用甜滨,在sdk寫了個(gè)死循環(huán)去調(diào)圖片上傳接口,并故意將照片設(shè)置為3M瘤袖,同時(shí)在idea的VM Option中JVM內(nèi)存調(diào)至300M衣摩,此時(shí)效果如下:
可以很清楚的發(fā)現(xiàn),old區(qū)增長速度特別快捂敌,同時(shí)gc次數(shù)頻繁艾扮,并且無法有效的降低old區(qū)占用,old區(qū)整體呈現(xiàn)遞增趨勢占婉,很容易發(fā)生內(nèi)存溢出泡嘴,經(jīng)過之前的定位流程,猜測為圖片本身較大锐涯,在亞當(dāng)區(qū)無法容納該對象時(shí)磕诊,直接塞到old區(qū)填物,同時(shí)加解密方法為靜態(tài)方法纹腌,被持續(xù)引用,導(dǎo)致無法進(jìn)行垃圾回收滞磺,導(dǎo)致old區(qū)持續(xù)遞增升薯。
定 案
處理方案:
生產(chǎn)服務(wù)器的內(nèi)存為8G,將堆內(nèi)存從2G擴(kuò)到4G
圖片上傳接口不在走通用加解密流程击困,在sdk涎劈、mapi單獨(dú)為其封裝了一套特殊的加解密流程广凸,base64串不進(jìn)行加密,直接做拼接處理蛛枚,其余參數(shù)做加解密谅海。處理后效果如下:
處理后可以很明顯的發(fā)現(xiàn)無論是Old Gen區(qū)的遞增速度還是gc次數(shù)相較于之前發(fā)生了很大的變化,趨于正常蹦浦。
案中案-CPU分析
以上過程其實(shí)問題已經(jīng)得到解決扭吁,但發(fā)現(xiàn)頻繁報(bào)fullgc的機(jī)器,cpu一直占用在10%以上盲镶,懷著打破砂鍋問到底的態(tài)度對cup的問題也進(jìn)行了下分析:
1侥袜、通過top命令查看占用cpu過高的進(jìn)程
可以看到占用cpu的進(jìn)程PID為7975
2、通過命令查找到占用cpu最高的線程
命令:top -H -p [進(jìn)程id] top –H –p 7975
3溉贿、將線程號轉(zhuǎn)化為16進(jìn)制(jstack線程堆棧中使用的16進(jìn)制)
printf "%x\n" [線程id]
4枫吧、 查找線程號對應(yīng)的線程
執(zhí)行: jstack [進(jìn)程id] |grep -A 10 [線程id的16進(jìn)制]
由上圖可以看到,一直在占用CPU的線程是CMS垃圾回收線程宇色,由于堆內(nèi)存占用過高程序又不釋放九杂,垃圾回收線程一直在嘗試回收內(nèi)存導(dǎo)致cpu過高。
并案分析-垃圾回收原因
上面再分析觸發(fā)垃圾回收的時(shí)候留了一個(gè)小尾巴代兵,為什么老年代和永久代占用不高的時(shí)候頻繁的發(fā)生了full gc呢尼酿。由于此應(yīng)用使用的是jdk1.6,垃圾回收器使用的是CMS植影,它是基于“標(biāo)記--清除”算法實(shí)現(xiàn)的裳擎,特點(diǎn)是在收集結(jié)束的時(shí)候會(huì)有大量的空間碎片產(chǎn)生∷急遥空間碎片太多的時(shí)候鹿响,將會(huì)給大對象的分配帶來很大的麻煩,往往會(huì)出現(xiàn)老年代還有很大的空間剩余谷饿,但是無法找到足夠大的連續(xù)空間來分配當(dāng)前對象的惶我,只能提前觸發(fā) full gc。如果jdk調(diào)整為1.7u4及以上即可使用G1垃圾回收算法不會(huì)產(chǎn)生大量的空間碎片博投。
結(jié) 案 總 結(jié)
JVM問題一般不是很容易遇到绸贡,程序有bug或者并發(fā)量大的時(shí)候均可能導(dǎo)致jvm異常,通過以上問題的分析過程及以往的經(jīng)驗(yàn)簡單總結(jié)下排查jvm問題的一般思路:
查看jvm內(nèi)存和機(jī)器CPU情況
內(nèi)存占用過高毅哗,可能是發(fā)生內(nèi)存泄漏听怕,需要導(dǎo)出dump文件借助mat或者是IBM HeapAnalyze來分析內(nèi)存中哪些對象占比比較高,那些實(shí)例較多的對象需要重點(diǎn)分析
cpu占用過高時(shí)可以通過步驟4的分析定位到具體的線程虑绵,程序編碼中用到多線程的地方一定要給線程起個(gè)有意義的名字不要用默認(rèn)的名字尿瞭,這樣出問題時(shí)方便定位。
上面只是個(gè)大概的流程翅睛,具體問題還需具體分析声搁,重點(diǎn)還是需要掌握jvm原理并靈活應(yīng)用黑竞。