這兩天由于公司需要, 自己編寫了一個(gè)用于接收dicom文件(醫(yī)學(xué)圖像文件)的server. 經(jīng)過(guò)各種coding-debuging-coding-debuging之后, 終于上線了, 上線后心里美滋滋的, 一切正常.
第二天一上班, 負(fù)責(zé)人和我說(shuō)接收太慢了, 卡的要死. 我想難道是python本身的問(wèn)題?(程序員本征思維)我好奇的打開(kāi)了終端輸入
找到進(jìn)程id:
即 21610
我這里還沒(méi)傳幾張圖片就到78m了, 看來(lái)是內(nèi)存問(wèn)題. 其實(shí)生產(chǎn)環(huán)境占用更多, 因?yàn)樯a(chǎn)環(huán)境保密所以只能在測(cè)試環(huán)境測(cè)試比較少的數(shù)據(jù), 生產(chǎn)環(huán)境曾一度上升到3.7g的內(nèi)存占用.
這樣果斷不行啊. 我發(fā)現(xiàn)有新的文件上傳之后內(nèi)存占用就會(huì)增大, 初步斷定是dicom文件相關(guān)對(duì)象占用的內(nèi)存. 現(xiàn)在的首要工作就是找到一個(gè)能進(jìn)行內(nèi)存泄露的調(diào)試工具了.
說(shuō)道這里可能大家會(huì)有疑問(wèn), python作為動(dòng)態(tài)類型語(yǔ)言同時(shí)擁有垃圾回收機(jī)怎么會(huì)有內(nèi)存泄露? 其實(shí)也有可能出現(xiàn)內(nèi)存泄露的情況, 有如下幾種:
-
對(duì)象一直被全局變量所引用, 全局變量生命周期長(zhǎng).
-
垃圾回收機(jī)被禁用或者設(shè)置成debug狀態(tài), 垃圾回收的內(nèi)存不會(huì)被釋放.
-
也是非常罕見(jiàn)的內(nèi)存泄露的方式就是今天遇到的問(wèn)題, 我周旋這個(gè)問(wèn)題兩天才debug出來(lái), 現(xiàn)在分享給大家.客官請(qǐng)您繼續(xù)往下看
說(shuō)到查看python內(nèi)存泄露的工具, 其實(shí)有挺多, 現(xiàn)在簡(jiǎn)短介紹一下
-
gc: python 內(nèi)置模塊, 函數(shù)少功能基本, 使用簡(jiǎn)單, 作為python開(kāi)發(fā)者里邊的內(nèi)容必須過(guò)一遍
-
objgraph: 可以繪制對(duì)象引用圖, 對(duì)于對(duì)象種類較少, 結(jié)構(gòu)比較簡(jiǎn)單的程序適用, 我這個(gè)一個(gè)庫(kù)套一個(gè)庫(kù), 內(nèi)存還用的這么多,
-
guppy: 可以對(duì)堆里邊的對(duì)象進(jìn)行統(tǒng)計(jì), 算是比較實(shí)用
-
pympler: 可以統(tǒng)計(jì)內(nèi)存里邊各種類型的使用, 獲取對(duì)象的大小
上邊這些雖然有用但是總是搞不到點(diǎn)子上, 上邊這些都需要改我的源程序, 比較費(fèi)勁, 線上的代碼不是說(shuō)改就能改的, 而且他們功能也都比較弱, 后來(lái)發(fā)現(xiàn)兩個(gè)強(qiáng)大的工具:
-
tracemalloc: 究極強(qiáng), 可以直接看到哪個(gè)(哪些)對(duì)象占用了最大的空間, 這些對(duì)象是誰(shuí), 調(diào)用棧是啥樣的, python3直接內(nèi)置, python2如果安裝的話需要編譯
-
pyrasite: 牛逼的第三方庫(kù), 可以滲透進(jìn)入正在運(yùn)行的python進(jìn)程動(dòng)態(tài)修改里邊的數(shù)據(jù)和代碼(其實(shí)修改代碼就是通過(guò)修改數(shù)據(jù)實(shí)現(xiàn))
我開(kāi)始的時(shí)候非常想用tracemalloc, 可是對(duì)python2特別不友好, 需要重新編譯python, 而且只能用python2.7.8編譯, 編譯好了也不容易嵌入到虛擬環(huán)境中, 頭大, 果斷換第二個(gè).
注: pyrasite使用之前需要在root用戶下運(yùn)行命令 echo 0 > /proc/sys/kernel/yama/ptrace_scope后才能正常使用
pyrasite里邊有一個(gè)工具叫pyrasite-memory-viewer, 功能和guppy差不多, 不過(guò)可以對(duì)內(nèi)存使用統(tǒng)計(jì)和對(duì)象之間的引用關(guān)系進(jìn)行快照保存, 很易用也很強(qiáng)大.運(yùn)行
pyrasite-memory-viewer
可以看到占用內(nèi)存最多的是DicomFileLike這種類型的對(duì)象.已經(jīng)達(dá)到上萬(wàn)個(gè), 這是不能忍受的.
就目前來(lái)看可能會(huì)有上邊說(shuō)的兩種內(nèi)存泄露原因?qū)е虏荒芑厥者@個(gè)對(duì)象.打開(kāi)
pyrasite-shellpyrasite-shell
我先通過(guò)
gc.isenabled()
判斷gc是否在工作, 結(jié)果發(fā)現(xiàn)是True, 也就是正常工作的, 而且使用gc.setdebug(gc.STATUS)設(shè)置gc為debug模式, 然后gc.collect()進(jìn)行垃圾回收發(fā)現(xiàn)并沒(méi)有更多內(nèi)存釋放,則否認(rèn)了第二種泄露的可能.
現(xiàn)在來(lái)看gc.garbage中不能被釋放的對(duì)象, 讓我來(lái)檢查一下是否有全局變量指向它們(這里極有可能是一個(gè)列表或者是一個(gè)字典)
gc.garbage 可以看到被塞滿了各種DicomFileLike對(duì)象
所以我們的目的就是先找到一個(gè)對(duì)象然后一級(jí)一級(jí)的向上尋找相互的引用.
到這里發(fā)現(xiàn)其實(shí)沒(méi)有更多的全局變量指向這個(gè)d了, 而且發(fā)現(xiàn)所以有的方法的對(duì)象地址和d是相同的, 說(shuō)明了這個(gè)對(duì)象其實(shí)是自循環(huán)引用的.
那么python不可能不支持循環(huán)引用對(duì)象的回收吧? 跟著這個(gè)問(wèn)題我查了一下stackoverflow
Does Python GC deal with reference-cycles like this?
這個(gè)問(wèn)題的第一個(gè)回答介紹的很清楚了, 如果用戶不自定類的__del__方法, gc可以回收帶有自引用的對(duì)象, 但是你自己實(shí)現(xiàn)了__del__方法就不行了.
這就是python內(nèi)存泄露的第三個(gè)可能.
回頭看DicomFileLike的源碼, 果然在__init__函數(shù)上方定義了一個(gè)__del__函數(shù), 我這里使用了一個(gè)猴子補(bǔ)丁刪除了這個(gè)方法, 內(nèi)存泄露的問(wèn)題就得以解決了.
總結(jié)
到這里整個(gè)調(diào)試過(guò)程就結(jié)束了, 然而實(shí)際上過(guò)程中做了很多曲折的工作, 在pyrasite中會(huì)找到幾個(gè)引用DicomFileLike對(duì)象的object, 比較不容易辨別, 最開(kāi)始我以為是某個(gè)全局的對(duì)象引用的DicomFileLike, 比如是列表什么的, 后來(lái)發(fā)現(xiàn)其實(shí)是locals()和globals()字典, 如果使用pyrasite-memory-viewer保存下來(lái)的數(shù)據(jù)會(huì)發(fā)現(xiàn)有一個(gè)大列表指向所有沒(méi)有回收的DicomFileLike對(duì)象, 捯飭半天發(fā)現(xiàn)其實(shí)是gc.garbage, 好囧, 曾讓我一度懷疑是第一種泄露方式, 但是怎么找這個(gè)對(duì)象都沒(méi)有找到. 其中還有幾次看到線程達(dá)到140+, 后來(lái)發(fā)現(xiàn)其實(shí)和線程一點(diǎn)關(guān)系沒(méi)有, 線程維持在這個(gè)數(shù)目上邊很穩(wěn)定.
在這個(gè)過(guò)程中用到的其他幾個(gè)hack的技巧有:
查看進(jìn)程的線程數(shù)量
根據(jù)對(duì)象的id/address動(dòng)態(tài)獲取對(duì)象
查看垃圾回收的日志
-
編程語(yǔ)言
+關(guān)注
關(guān)注
10文章
1950瀏覽量
34985 -
python
+關(guān)注
關(guān)注
56文章
4807瀏覽量
85038
原文標(biāo)題:記一次調(diào)試python內(nèi)存泄露的問(wèn)題
文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
超聲波焊接常見(jiàn)問(wèn)題解決方案
Triton編譯器的常見(jiàn)問(wèn)題解決方案
【電磁兼容技術(shù)案例分享】屏蔽罩結(jié)構(gòu)縫隙導(dǎo)致輻射發(fā)射問(wèn)題解決案例
![【電磁兼容技術(shù)案例分享】屏蔽罩結(jié)構(gòu)縫隙導(dǎo)致輻射發(fā)射<b class='flag-5'>問(wèn)題解決</b>案例](https://file.elecfans.com/web2/M00/3F/CE/pYYBAGJqCX2AbtM8AAANJ1_N7GA875.jpg)
C2000 F28004x系列MCU PLL鎖相失敗問(wèn)題解決方案
![C2000 F28004x系列MCU PLL鎖相失敗<b class='flag-5'>問(wèn)題解決方案</b>](https://file.elecfans.com/web1/M00/D9/4E/pIYBAF_1ac2Ac0EEAABDkS1IP1s689.png)
【電磁兼容技術(shù)案例分享】高低壓電纜混合布線導(dǎo)致的傳導(dǎo)問(wèn)題解決案例
![【電磁兼容技術(shù)案例分享】高低壓電纜混合布線導(dǎo)致的傳導(dǎo)<b class='flag-5'>問(wèn)題解決</b>案例](https://file.elecfans.com/web2/M00/3F/CE/pYYBAGJqCX2AbtM8AAANJ1_N7GA875.jpg)
【電磁兼容技術(shù)案例分享】對(duì)地電容接地點(diǎn)差異導(dǎo)致輻射發(fā)射問(wèn)題解決案例
![【電磁兼容技術(shù)案例分享】對(duì)地電容接地點(diǎn)差異導(dǎo)致輻射發(fā)射<b class='flag-5'>問(wèn)題解決</b>案例](https://file.elecfans.com/web2/M00/3F/CE/pYYBAGJqCX2AbtM8AAANJ1_N7GA875.jpg)
EMC問(wèn)題解決實(shí)戰(zhàn)教學(xué)4——CS抗擾度問(wèn)題解決!
![EMC<b class='flag-5'>問(wèn)題解決</b>實(shí)戰(zhàn)教學(xué)4——CS抗擾度<b class='flag-5'>問(wèn)題解決</b>!](https://file.elecfans.com/web2/M00/3F/CE/pYYBAGJqCX2AbtM8AAANJ1_N7GA875.jpg)
EMC問(wèn)題解決實(shí)戰(zhàn)教學(xué)2——傳導(dǎo)發(fā)射問(wèn)題解決!
![EMC<b class='flag-5'>問(wèn)題解決</b>實(shí)戰(zhàn)教學(xué)2——傳導(dǎo)發(fā)射<b class='flag-5'>問(wèn)題解決</b>!](https://file.elecfans.com/web2/M00/3F/CE/pYYBAGJqCX2AbtM8AAANJ1_N7GA875.jpg)
EMC問(wèn)題解決實(shí)戰(zhàn)教學(xué)1——輻射發(fā)射問(wèn)題解決!
![EMC<b class='flag-5'>問(wèn)題解決</b>實(shí)戰(zhàn)教學(xué)1——輻射發(fā)射<b class='flag-5'>問(wèn)題解決</b>!](https://file.elecfans.com/web2/M00/3F/CE/pYYBAGJqCX2AbtM8AAANJ1_N7GA875.jpg)
淺談公網(wǎng)無(wú)信號(hào)區(qū)域遠(yuǎn)程抄表問(wèn)題解決方案及產(chǎn)品選型
![淺談公網(wǎng)無(wú)信號(hào)區(qū)域遠(yuǎn)程抄表<b class='flag-5'>問(wèn)題解決方案</b>及產(chǎn)品選型](https://file1.elecfans.com//web2/M00/C1/3B/wKgaomXUVfuAAT4nAACHP1DVsys98.jpeg)
記一次詭異的內(nèi)存泄漏
![<b class='flag-5'>記</b><b class='flag-5'>一次</b>詭異的<b class='flag-5'>內(nèi)存</b>泄漏](https://file1.elecfans.com/web2/M00/C1/0E/wKgaomXS62WAFKKUAAAaxvr3Vec838.png)
【電磁兼容技術(shù)案例分享】由SGMII通訊導(dǎo)致的輻射發(fā)射高頻單支超標(biāo)問(wèn)題解決案例
![【電磁兼容技術(shù)案例分享】由SGMII通訊導(dǎo)致的輻射發(fā)射高頻單支超標(biāo)<b class='flag-5'>問(wèn)題解決</b>案例](https://file.elecfans.com/web2/M00/3F/CE/pYYBAGJqCX2AbtM8AAANJ1_N7GA875.jpg)
評(píng)論