python內存泄露memory leak排查記錄


問題描述

A服務,是一個檢測MGR集群主節點是否發生變化的服務,使用python語言實現的。
針對每個集群,主線程會創建一個子線程,並由子線程去檢測。子線程會頻繁的創建和銷毀。

上線以后,由於經常會有功能發布,從而重啟服務,開始一段時間沒有發現問題。
半個月前的周二服務發布后,大約一周時間,沒有再發布。到周末的時候,突然告警系統負載高,經過排查,發現內存幾乎耗盡,並查到是A服務占用巨大內存,沒有釋放。

排查過程

已經確定,A服務是存在內存泄露的,到底是什么地方內存使用完,卻沒有釋放呢?
這是一個令人頭疼的問題,以前確實沒有遇到過Python的內存泄露。

首先,網上搜索關於python內存泄漏的問題。大體了解到,Python的內存回收是基於引用計數的,也就是說,如果某個對象被使用一次,引用計數就會增加1。對象的引用計數為0時,內存就會被回收掉。

常見的導致內存泄露的情況有兩種:

  • (1)對象一直被全局變量使用,全局變量生命周期比較長,所以內存一直得不到釋放。
  • (2)循環引用中的對象定義了__del__的情況.

網上提供了各種用於排查內存泄露的工具,例如objgraph、guppy、pympler等,其具體使用參考文后的鏈接。

看了半天這些工具的使用,感覺還是應該看看自己代碼,是不是存在對象使用完,但是一直被引用的情況。

首先,排查內存泄露的位置是在主線程還是子線程。通過查看,發現「子線程一直在執行」與「子線程頻繁創建和退出」兩種情況下,內存消耗差別較大, 而且「子線程一直在執行」內存消耗很小。這樣,就可以定位到,內存泄露位置是在主線程或「子線程loop之前的代碼」。

接着,屏蔽子線程,發現內存正常。

所以,定位到問題是在「子線程loop之前的代碼」中。
最后,發現是頻繁調用第三方包的函數導致的。

解決辦法

找到問題的原因了,那么解決方法就好辦了。改用其他的包或修改使用方式,繞開這個大坑。

參考

一次調試python內存泄露的問題
使用gc、objgraph干掉python內存泄露與循環引用!
Python內存優化:Profile,slots,compact dict


免責聲明!

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



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