阿里雲OSSClient使用不當引起的OOM問題分析


一、背景

最近新服務上線,運行了一段時間都很平穩,沒有出現什么大的異常,突然有一天運維同事通知說注冊中心上服務掉線了。於是登錄了發生異常服務的組件,查看日志信息,關鍵信息如圖:

 

 從上面兩個圖片可以簡單了解到,

1、應該是服務發生了OOM異常
2、Consul鏈接因為Connection pool shut down而鏈接失敗。

二、問題分析

猜想1:是不是因為Consul服務宕機,導致服務鏈接不上,並且短時間重試多次造成OOM?
實際:整個錯誤持續了幾個小時,consul服務宕機其他服務也應該收到影響,但是其他服務並沒有問題。所以猜想1應該不對。

猜想2:Connection pool shut down 這個問題是因為什么產生的?是不是可以從該地方入手,根據圖中的異常位置可以查看代碼。查看對應的

 繼續查看


所以一定是因為 isShutDown 這個狀態值變為了true,才導致打印對應的錯誤信息的。

繼續查找shutdown可能賦值為true的地方

 

最終定位到能夠調用shutdown只有finalize()或者close()。

所以猜想一定是發生OOM之后,GC在回收內存的時候調用了finalize()方法,繼而引發的isShutDown變為true。

 

猜想3:我們是否可以通過dump文件,查看內存泄漏的地方,進而在本地模擬這個場景。找到運維下載OOM時候的dump文件,經過MAT工具一番查看,可以很明顯的找到內存泄漏的點。

如圖所示:據dump文件描述,PoolingHttpClientConnectionManager 這個對象居然占到了整個大對象內存的90%多,並且是被一個數組對象引用着的,引用的對象數量居然有60萬。繼續查看詳細:可以看到這個對象應該是在 com.aliyun.oss.common.comm.IdleConnectionReaper 這個類當中,我們可以看下這個類的源碼,可以確認應該就是下面這個靜態對象列表。

 

 

查看該列表使用的地方,會發現在registerConnectionManager方法內進行填充

registerConnectionManager方法調用的地方,

 最終定位,果然是在創建OSSClient的時候調用的

OssClient構造函數

我們看下OssClient是如何創建的,為什么connectionManagers列表會持有這么多對象。最終發現在上傳圖片的過程中,我們每調用一次getOSSClient()就會生成一個OssClient對象,就會在connectionManagers添加一個PoolingHttpClientConnectionManager對象。

最終問題定位應該就是這里,因為沒有及時關閉OssClient導致的內存泄漏,因為不是一瞬間就造成內存暴漲,而是緩慢的增加,所以沒有第一時間發現,這也是為什么重啟之后就沒有報錯的原因。

 

三、問題重現
我們本地啟動服務,修改一下jvm參數如下(比線上減少了10倍)
-Xms200M -Xmx200M -Xss256K

順便增加一些測試代碼,打印一下當前jvm的內存信息

啟動程序,模擬線上上傳圖片:

隨着請求數量增加,可以發現connectionManagers中PoolingHttpClientConnectionManager對象也在不斷上漲。

 加大請求次數,通過VirtualGC查看內存分布,也可以發現 老年代一直在上漲,幾次GC之后也沒有明顯下降

最終在我們還可以看到

1、出現了OOM異常

2、Consul的 Connection pool shut down 也被觸發了

綜上所有的跡象都說明就是因為OSSClient這個類被一直new,導致內存不斷泄漏、最終導致程序OOM使得出現了很多異常。

 

四、問題解決

因為本服務需要提供用戶上傳圖片到阿里雲OSS的功能,並且調用頻次還算比較高,所以解決方式有兩種。
1、每次new OSSClient之后,需要調用shutdown方法,移除PoolingHttpClientConnectionManager對象。

 

 

我們都知道new操作是一個比較耗費資源和性能的操作,而且什么時候shutdown不好控制,所以我覺得這樣的方式比較適用於操作比較少的場合。

2、使用單例的OSSClient,看了剛剛的代碼OssClient本身也封裝了一個PoolingHttpClientConnectionManager對象,說明它本身就是支持鏈接池的,也就是可以並發訪問。

修正為單例之后,執行測試:

可以看到經過一段時間的請求,整體老年代內存維持不高。

四、問題總結

經過這個bug的排查,可以有以下幾點收獲:
1、遇到OOM問題,僅憑借日志輸出可能不能馬上定位到問題的本質,最好可以通過dump文件進行分析
2、像HttpClient這種需要建立socket鏈接的資源,在使用的時候盡量能夠池化,避免大量創建對象耗費資源,並且使用后要考慮關閉的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM