0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

記一次調(diào)試python內(nèi)存泄露的問題解決方案分享

馬哥Linux運(yùn)維 ? 2017-12-18 16:55 ? 次閱讀

這兩天由于公司需要, 自己編寫了一個(gè)用于接收dicom文件(醫(yī)學(xué)圖像文件)的server. 經(jīng)過各種coding-debuging-coding-debuging之后, 終于上線了, 上線后心里美滋滋的, 一切正常.

第二天一上班, 負(fù)責(zé)人和我說接收太慢了, 卡的要死. 我想難道是python本身的問題?(程序員本征思維)我好奇的打開了終端輸入

找到進(jìn)程id:

即 21610

我這里還沒傳幾張圖片就到78m了, 看來是內(nèi)存問題. 其實(shí)生產(chǎn)環(huán)境占用更多, 因?yàn)樯a(chǎn)環(huán)境保密所以只能在測試環(huán)境測試比較少的數(shù)據(jù), 生產(chǎn)環(huán)境曾一度上升到3.7g的內(nèi)存占用.

這樣果斷不行啊. 我發(fā)現(xiàn)有新的文件上傳之后內(nèi)存占用就會(huì)增大, 初步斷定是dicom文件相關(guān)對象占用的內(nèi)存. 現(xiàn)在的首要工作就是找到一個(gè)能進(jìn)行內(nèi)存泄露的調(diào)試工具了.

說道這里可能大家會(huì)有疑問, python作為動(dòng)態(tài)類型語言同時(shí)擁有垃圾回收機(jī)怎么會(huì)有內(nèi)存泄露? 其實(shí)也有可能出現(xiàn)內(nèi)存泄露的情況, 有如下幾種:

  1. 對象一直被全局變量所引用, 全局變量生命周期長.

  2. 垃圾回收機(jī)被禁用或者設(shè)置成debug狀態(tài), 垃圾回收的內(nèi)存不會(huì)被釋放.

  3. 也是非常罕見的內(nèi)存泄露的方式就是今天遇到的問題, 我周旋這個(gè)問題兩天才debug出來, 現(xiàn)在分享給大家.客官請您繼續(xù)往下看

說到查看python內(nèi)存泄露的工具, 其實(shí)有挺多, 現(xiàn)在簡短介紹一下

  • gc: python 內(nèi)置模塊, 函數(shù)少功能基本, 使用簡單, 作為python開發(fā)者里邊的內(nèi)容必須過一遍

  • objgraph: 可以繪制對象引用圖, 對于對象種類較少, 結(jié)構(gòu)比較簡單的程序適用, 我這個(gè)一個(gè)庫套一個(gè)庫, 內(nèi)存還用的這么多,

  • guppy: 可以對堆里邊的對象進(jìn)行統(tǒng)計(jì), 算是比較實(shí)用

  • pympler: 可以統(tǒng)計(jì)內(nèi)存里邊各種類型的使用, 獲取對象的大小

上邊這些雖然有用但是總是搞不到點(diǎn)子上, 上邊這些都需要改我的源程序, 比較費(fèi)勁, 線上的代碼不是說改就能改的, 而且他們功能也都比較弱, 后來發(fā)現(xiàn)兩個(gè)強(qiáng)大的工具:

  • tracemalloc: 究極強(qiáng), 可以直接看到哪個(gè)(哪些)對象占用了最大的空間, 這些對象是誰, 調(diào)用棧是啥樣的, python3直接內(nèi)置, python2如果安裝的話需要編譯

  • pyrasite: 牛逼的第三方庫, 可以滲透進(jìn)入正在運(yùn)行的python進(jìn)程動(dòng)態(tài)修改里邊的數(shù)據(jù)和代碼(其實(shí)修改代碼就是通過修改數(shù)據(jù)實(shí)現(xiàn))

我開始的時(shí)候非常想用tracemalloc, 可是對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差不多, 不過可以對內(nèi)存使用統(tǒng)計(jì)和對象之間的引用關(guān)系進(jìn)行快照保存, 很易用也很強(qiáng)大.運(yùn)行

pyrasite-memory-viewer

記一次調(diào)試python內(nèi)存泄露的問題解決方案分享

可以看到占用內(nèi)存最多的是DicomFileLike這種類型的對象.已經(jīng)達(dá)到上萬個(gè), 這是不能忍受的.

就目前來看可能會(huì)有上邊說的兩種內(nèi)存泄露原因?qū)е虏荒芑厥者@個(gè)對象.打開

pyrasite-shellpyrasite-shell

我先通過

gc.isenabled()

判斷gc是否在工作, 結(jié)果發(fā)現(xiàn)是True, 也就是正常工作的, 而且使用gc.setdebug(gc.STATUS)設(shè)置gc為debug模式, 然后gc.collect()進(jìn)行垃圾回收發(fā)現(xiàn)并沒有更多內(nèi)存釋放,則否認(rèn)了第二種泄露的可能.

現(xiàn)在來看gc.garbage中不能被釋放的對象, 讓我來檢查一下是否有全局變量指向它們(這里極有可能是一個(gè)列表或者是一個(gè)字典)

gc.garbage 可以看到被塞滿了各種DicomFileLike對象

記一次調(diào)試python內(nèi)存泄露的問題解決方案分享

所以我們的目的就是先找到一個(gè)對象然后一級一級的向上尋找相互的引用.

記一次調(diào)試python內(nèi)存泄露的問題解決方案分享

到這里發(fā)現(xiàn)其實(shí)沒有更多的全局變量指向這個(gè)d了, 而且發(fā)現(xiàn)所以有的方法的對象地址和d是相同的, 說明了這個(gè)對象其實(shí)是自循環(huán)引用的.

那么python不可能不支持循環(huán)引用對象的回收吧? 跟著這個(gè)問題我查了一下stackoverflow

Does Python GC deal with reference-cycles like this?

這個(gè)問題的第一個(gè)回答介紹的很清楚了, 如果用戶不自定類的__del__方法, gc可以回收帶有自引用的對象, 但是你自己實(shí)現(xiàn)了__del__方法就不行了.

這就是python內(nèi)存泄露的第三個(gè)可能.

回頭看DicomFileLike的源碼, 果然在__init__函數(shù)上方定義了一個(gè)__del__函數(shù), 我這里使用了一個(gè)猴子補(bǔ)丁刪除了這個(gè)方法, 內(nèi)存泄露的問題就得以解決了.

總結(jié)

到這里整個(gè)調(diào)試過程就結(jié)束了, 然而實(shí)際上過程中做了很多曲折的工作, 在pyrasite中會(huì)找到幾個(gè)引用DicomFileLike對象的object, 比較不容易辨別, 最開始我以為是某個(gè)全局的對象引用的DicomFileLike, 比如是列表什么的, 后來發(fā)現(xiàn)其實(shí)是locals()和globals()字典, 如果使用pyrasite-memory-viewer保存下來的數(shù)據(jù)會(huì)發(fā)現(xiàn)有一個(gè)大列表指向所有沒有回收的DicomFileLike對象, 捯飭半天發(fā)現(xiàn)其實(shí)是gc.garbage, 好囧, 曾讓我一度懷疑是第一種泄露方式, 但是怎么找這個(gè)對象都沒有找到. 其中還有幾次看到線程達(dá)到140+, 后來發(fā)現(xiàn)其實(shí)和線程一點(diǎn)關(guān)系沒有, 線程維持在這個(gè)數(shù)目上邊很穩(wěn)定.

在這個(gè)過程中用到的其他幾個(gè)hack的技巧有:

查看進(jìn)程的線程數(shù)量

根據(jù)對象的id/address動(dòng)態(tài)獲取對象

查看垃圾回收的日志


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 編程語言
    +關(guān)注

    關(guān)注

    10

    文章

    1916

    瀏覽量

    34378
  • python
    +關(guān)注

    關(guān)注

    53

    文章

    4753

    瀏覽量

    84077

原文標(biāo)題:記一次調(diào)試python內(nèi)存泄露的問題

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    【電磁兼容技術(shù)案例分享】高低壓電纜混合布線導(dǎo)致的傳導(dǎo)問題解決案例

    【電磁兼容技術(shù)案例分享】高低壓電纜混合布線導(dǎo)致的傳導(dǎo)問題解決案例
    的頭像 發(fā)表于 09-12 08:05 ?127次閱讀
    【電磁兼容技術(shù)案例分享】高低壓電纜混合布線導(dǎo)致的傳導(dǎo)<b class='flag-5'>問題解決</b>案例

    【電磁兼容技術(shù)案例分享】對地電容接地點(diǎn)差異導(dǎo)致輻射發(fā)射問題解決案例

    【電磁兼容技術(shù)案例分享】對地電容接地點(diǎn)差異導(dǎo)致輻射發(fā)射問題解決案例
    的頭像 發(fā)表于 08-30 12:30 ?127次閱讀
    【電磁兼容技術(shù)案例分享】對地電容接地點(diǎn)差異導(dǎo)致輻射發(fā)射<b class='flag-5'>問題解決</b>案例

    EMC問題解決實(shí)戰(zhàn)教學(xué)4——CS抗擾度問題解決!

    EMC問題解決之實(shí)戰(zhàn)教學(xué)SES”CS抗擾度測試中,工程師常常會(huì)面對信號(hào)干擾、復(fù)雜的噪聲模型、差共模干擾判斷困難以及測試設(shè)備和方法選擇等挑戰(zhàn)。這些挑戰(zhàn)不僅影響了測試的準(zhǔn)確性和可靠性,同時(shí)也增加了工程師
    的頭像 發(fā)表于 06-04 08:17 ?644次閱讀
    EMC<b class='flag-5'>問題解決</b>實(shí)戰(zhàn)教學(xué)4——CS抗擾度<b class='flag-5'>問題解決</b>!

    EMC問題解決實(shí)戰(zhàn)教學(xué)2——傳導(dǎo)發(fā)射問題解決

    EMC問題解決之實(shí)戰(zhàn)教學(xué)SES”信號(hào)干擾、復(fù)雜的噪聲模型、差共模干擾判斷困難以及測試設(shè)備和方法選擇都是工程師在傳導(dǎo)發(fā)射測試中常遇到的挑戰(zhàn)。這些困難不僅影響了測試的準(zhǔn)確性和可靠性,同時(shí)也增加了工程師
    的頭像 發(fā)表于 05-28 08:17 ?370次閱讀
    EMC<b class='flag-5'>問題解決</b>實(shí)戰(zhàn)教學(xué)2——傳導(dǎo)發(fā)射<b class='flag-5'>問題解決</b>!

    EMC問題解決實(shí)戰(zhàn)教學(xué)1——輻射發(fā)射問題解決!

    EMC問題解決之實(shí)戰(zhàn)教學(xué)SES”輻射測試是個(gè)充滿挑戰(zhàn)的領(lǐng)域,工程師常常需要面對多種設(shè)備和復(fù)雜的測試流程,例如在設(shè)備選擇和測試方法方面遇到各種挑戰(zhàn)。在龐大且復(fù)雜的輻射數(shù)據(jù)也增加了數(shù)據(jù)解讀的難度
    的頭像 發(fā)表于 05-26 08:17 ?261次閱讀
    EMC<b class='flag-5'>問題解決</b>實(shí)戰(zhàn)教學(xué)1——輻射發(fā)射<b class='flag-5'>問題解決</b>!

    【電磁兼容技術(shù)案例分享】智能門禁的ESD問題解決案例

    【電磁兼容技術(shù)案例分享】智能門禁的ESD問題解決案例
    的頭像 發(fā)表于 04-19 08:16 ?257次閱讀
    【電磁兼容技術(shù)案例分享】智能門禁的ESD<b class='flag-5'>問題解決</b>案例

    淺談公網(wǎng)無信號(hào)區(qū)域遠(yuǎn)程抄表問題解決方案及產(chǎn)品選型

    淺談公網(wǎng)無信號(hào)區(qū)域遠(yuǎn)程抄表問題解決方案及產(chǎn)品選型 張穎姣 安科瑞電氣股份有限公司 上海嘉定 201801 摘要:隨著計(jì)量自動(dòng)化系統(tǒng)的逐步完善,電網(wǎng)全用戶表碼信息采集成為系統(tǒng)數(shù)據(jù)得以深化應(yīng)用的重要
    的頭像 發(fā)表于 02-20 15:34 ?427次閱讀
    淺談公網(wǎng)無信號(hào)區(qū)域遠(yuǎn)程抄表<b class='flag-5'>問題解決方案</b>及產(chǎn)品選型

    一次詭異的內(nèi)存泄漏

    最近在補(bǔ)些基礎(chǔ)知識(shí),恰好涉及到了智能指針std::weak_ptr在解決std::shared_ptr時(shí)候循環(huán)引用的問題
    的頭像 發(fā)表于 02-19 13:44 ?506次閱讀
    <b class='flag-5'>記</b><b class='flag-5'>一次</b>詭異的<b class='flag-5'>內(nèi)存</b>泄漏

    【電磁兼容技術(shù)案例分享】由SGMII通訊導(dǎo)致的輻射發(fā)射高頻單支超標(biāo)問題解決案例

    【電磁兼容技術(shù)案例分享】由SGMII通訊導(dǎo)致的輻射發(fā)射高頻單支超標(biāo)問題解決案例
    的頭像 發(fā)表于 02-19 13:20 ?659次閱讀
    【電磁兼容技術(shù)案例分享】由SGMII通訊導(dǎo)致的輻射發(fā)射高頻單支超標(biāo)<b class='flag-5'>問題解決</b>案例

    PCB壓合問題解決方法

    PCB壓合問題解決方法
    的頭像 發(fā)表于 01-05 10:32 ?783次閱讀

    【電磁兼容技術(shù)案例分享】某控制器產(chǎn)品傳導(dǎo)電壓法超標(biāo)問題解決案例

    【電磁兼容技術(shù)案例分享】某控制器產(chǎn)品傳導(dǎo)電壓法超標(biāo)問題解決案例
    的頭像 發(fā)表于 01-05 08:16 ?723次閱讀
    【電磁兼容技術(shù)案例分享】某控制器產(chǎn)品傳導(dǎo)電壓法超標(biāo)<b class='flag-5'>問題解決</b>案例

    內(nèi)存溢出與內(nèi)存泄漏:定義、區(qū)別與解決方案

    內(nèi)存溢出與內(nèi)存泄漏:定義、區(qū)別與解決方案? 內(nèi)存溢出和內(nèi)存泄漏是計(jì)算機(jī)科學(xué)中常見的問題,在開發(fā)和調(diào)試
    的頭像 發(fā)表于 12-19 14:10 ?1816次閱讀

    全志R128內(nèi)存泄露調(diào)試案例

    產(chǎn)生了內(nèi)存泄漏。 根本原因 播放器老化過程中,只調(diào)用AudioTrackCreate一次,循環(huán)播放時(shí)會(huì)多次調(diào)用_AudioTrackStart,最后退出播放才調(diào)用AudioTrackDestroy銷毀
    發(fā)表于 12-11 10:57

    內(nèi)存是如何泄露

    作為 C++ 程序員,內(nèi)存泄露始終是懸在頭上的顆炸彈。在過去幾年的 C++ 開發(fā)過程中,由于我們采用了些技術(shù),我們的程序發(fā)生內(nèi)存
    的頭像 發(fā)表于 11-13 14:13 ?348次閱讀
    <b class='flag-5'>內(nèi)存</b>是如何<b class='flag-5'>泄露</b>的

    總結(jié):30個(gè)單片機(jī)常見問題解決辦法!

    總結(jié):30個(gè)單片機(jī)常見問題解決辦法!
    的頭像 發(fā)表于 10-17 17:46 ?2868次閱讀
    總結(jié):30個(gè)單片機(jī)常見<b class='flag-5'>問題解決</b>辦法!