圖片為證
圖片上為問題代碼

問題代碼.jpg
1.代碼解讀
一句話解讀:通過oss sdk獲取oss私有圖片臨時鏈接
2.問題發(fā)現
-
日常巡檢
- 發(fā)現sentinel后臺服務實例pod有掉線情況
- k8s后臺確認服務異常,超過設置彈性伸縮指標
-
阿里后臺
- fullgc頻繁(新服務上線后未關注)
- cpu、負載間斷升高(fullgc引起)
-
項目重啟后內存持續(xù)升高(途中幾段都為重啟后運行)
阿里后臺發(fā)現1.jpg
阿里后臺發(fā)現2.jpg
3.解決過程
- 1.根據阿里后臺現象判斷有明顯內存泄漏
- 2.找運維同學查看jvm指標,導出dump文件
查看堆中內存占用最大的對象,同時觸發(fā)手動FullGC
jmap -histo:live <pid>|head
導出dump文件
jmap -dump:format=b,file=name.hprof <pid>
- 3.后讓運維同學重啟服務并升級服務配置:1c2g->2g4G,避免短期頻繁觸發(fā)fullgc、觸發(fā)彈性伸縮
dump文件分析
我使用的是jprofiler破解版,導入即可查看離線jvm堆棧信息
-
1.jvm堆中實例數排序
實例數排序.jpg- 開始我一直在關注這個排序結果,最多的實例為HashMap$Node,是個反射對象
- 繼續(xù)查看對象根實例,發(fā)現很凌亂,根里面什么信息都有,看不出哪里內存泄漏
- 根據上面的結果初步判定是代碼中序列化出現了內存泄漏(關注的點和判斷結果導致排查方向錯誤)
- 于是去排查了代碼(不知道哪里的問題,嘗試解決)
- 線程池核心線程配置不符合實際
- 發(fā)現fastjson和參考服務版本不一致,網上也有類似案例:序列化要配置,全局共享實例SerializeConfig.globalInstance
- restTemplate未指定連接池
- 調整線程池、升級fastjson版本、指定restTemplate連接池,處理后上線,觀察后并未得到解決
-
反查了fastjson兩個版本1.2.58,1.2.62,已經默認是全局共享實例了(改這個版本實際啥用也沒有)
fastjson1.jpg
fastjson2.jpg
- ======================================================
- 前面所有嘗試無效后,又去查代碼,發(fā)現可疑代碼片段(見開頭)
- 經過優(yōu)化ossclient為單例、測試,初步定位就是oss連接無限創(chuàng)建,沒有關閉導致的內存泄漏
優(yōu)化代碼.jpg
- 引發(fā)了疑問:jprofiler為啥看不出是http連接池問題導致內存泄漏?下圖是找同事eclipse MAT分析查看結果:直接指出了問題所在
eclipse1.jpgeclipse2.jpg -
2.jvm堆中保留對象排序
保留對象排序.jpg

傳入引用gc根.jpg

禍根.jpg
- jprofiler點擊【保留大小】排序,其實也很容易發(fā)現問題,常見對象一般先忽略,可見異常對象:PoolingHttpClientConnectionManager
- 點擊【顯示到GC根的路徑】,PoolingHttpClientConnectionManager整個大對象被一個數組對象引用著的,根路徑為:com.aliyun.oss.common.comm.IdleConnectionReaper
- IdleConnectionReaper里面有個【靜態(tài)數組】對象,就是用來放連接對象的,問題代碼剛好未關閉連接,導致無限增長
4.總結
- 1.內存泄漏本身不是特別難處理的問題,解決過程一般來說都是固定程序(當然,也會遇到排除特別難發(fā)現的)
- 2.熟悉相關分析工具能事半功倍,案例中我就是因為不熟悉工具導致盲目嘗試過,但是盲目過程中也優(yōu)化了部分代碼
5.思考
本次內存泄漏是ossclient連接無限創(chuàng)建并未關閉導致,為什么從jprofiler看HashMap$Node實列數非常大?








