問題描述
A服務,是一個檢測MGR集羣主節點是否發生變化的服務,使用python語言實現的。 針對每一個集羣,主線程會建立一個子線程,並由子線程去檢測。子線程會頻繁的建立和銷燬。html
上線之後,因爲常常會有功能發佈,從而重啓服務,開始一段時間沒有發現問題。 半個月前的週二服務發佈後,大約一週時間,沒有再發布。到週末的時候,忽然告警系統負載高,通過排查,發現內存幾乎耗盡,並查到是A服務佔用巨大內存,沒有釋放。python
排查過程
已經肯定,A服務是存在內存泄露的,究竟是什麼地方內存使用完,卻沒有釋放呢? 這是一個使人頭疼的問題,之前確實沒有遇到過Python的內存泄露。函數
首先,網上搜索關於python內存泄漏的問題。大致瞭解到,Python的內存回收是基於引用計數的,也就是說,若是某個對象被使用一次,引用計數就會增長1。對象的引用計數爲0時,內存就會被回收掉。工具
常見的致使內存泄露的狀況有兩種:oop
- (1)對象一直被全局變量使用,全局變量生命週期比較長,因此內存一直得不到釋放。
- (2)循環引用中的對象定義了__del__的狀況.
網上提供了各類用於排查內存泄露的工具,例如objgraph、guppy、pympler等,其具體使用參考文後的連接。優化
看了半天這些工具的使用,感受仍是應該看看本身代碼,是否是存在對象使用完,可是一直被引用的狀況。spa
首先,排查內存泄露的位置是在主線程仍是子線程。經過查看,發現「子線程一直在執行」與「子線程頻繁建立和退出」兩種狀況下,內存消耗差異較大, 並且「子線程一直在執行」內存消耗很小。這樣,就能夠定位到,內存泄露位置是在主線程或「子線程loop以前的代碼」。線程
接着,屏蔽子線程,發現內存正常。調試
因此,定位到問題是在「子線程loop以前的代碼」中。 最後,發現是頻繁調用第三方包的函數致使的。htm
解決辦法
找到問題的緣由了,那麼解決方法就好辦了。改用其餘的包或修改使用方式,繞開這個大坑。
參考
一次調試python內存泄露的問題 使用gc、objgraph幹掉python內存泄露與循環引用! Python內存優化:Profile,slots,compact dict