0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

记一次调试python内存泄露的问题解决方案分享

马哥Linux运维 2017-12-18 16:55 次阅读

这两天由于公司需要, 自己编写了一个用于接收dicom文件(医学图像文件)的server. 经过各种coding-debuging-coding-debuging之后, 终于上线了, 上线后心里美滋滋的, 一切正常.

第二天一上班, 负责人和我说接收太慢了, 卡的要死. 我想难道是python本身的问题?(程序员本征思维)我好奇的打开了终端输入

找到进程id:

即 21610

我这里还没传几张图片就到78m了, 看来是内存问题. 其实生产环境占用更多, 因为生产环境保密所以只能在测试环境测试比较少的数据, 生产环境曾一度上升到3.7g的内存占用.

这样果断不行啊. 我发现有新的文件上传之后内存占用就会增大, 初步断定是dicom文件相关对象占用的内存. 现在的首要工作就是找到一个能进行内存泄露的调试工具了.

说道这里可能大家会有疑问, python作为动态类型语言同时拥有垃圾回收机怎么会有内存泄露? 其实也有可能出现内存泄露的情况, 有如下几种:

  1. 对象一直被全局变量所引用, 全局变量生命周期长.

  2. 垃圾回收机被禁用或者设置成debug状态, 垃圾回收的内存不会被释放.

  3. 也是非常罕见的内存泄露的方式就是今天遇到的问题, 我周旋这个问题两天才debug出来, 现在分享给大家.客官请您继续往下看

说到查看python内存泄露的工具, 其实有挺多, 现在简短介绍一下

  • gc: python 内置模块, 函数少功能基本, 使用简单, 作为python开发者里边的内容必须过一遍

  • objgraph: 可以绘制对象引用图, 对于对象种类较少, 结构比较简单的程序适用, 我这个一个库套一个库, 内存还用的这么多,

  • guppy: 可以对堆里边的对象进行统计, 算是比较实用

  • pympler: 可以统计内存里边各种类型的使用, 获取对象的大小

上边这些虽然有用但是总是搞不到点子上, 上边这些都需要改我的源程序, 比较费劲, 线上的代码不是说改就能改的, 而且他们功能也都比较弱, 后来发现两个强大的工具:

  • tracemalloc: 究极强, 可以直接看到哪个(哪些)对象占用了最大的空间, 这些对象是谁, 调用栈是啥样的, python3直接内置, python2如果安装的话需要编译

  • pyrasite: 牛逼的第三方库, 可以渗透进入正在运行的python进程动态修改里边的数据和代码(其实修改代码就是通过修改数据实现)

我开始的时候非常想用tracemalloc, 可是对python2特别不友好, 需要重新编译python, 而且只能用python2.7.8编译, 编译好了也不容易嵌入到虚拟环境中, 头大, 果断换第二个.

注: pyrasite使用之前需要在root用户下运行命令 echo 0 > /proc/sys/kernel/yama/ptrace_scope后才能正常使用

pyrasite里边有一个工具叫pyrasite-memory-viewer, 功能和guppy差不多, 不过可以对内存使用统计和对象之间的引用关系进行快照保存, 很易用也很强大.运行

pyrasite-memory-viewer

记一次调试python内存泄露的问题解决方案分享

可以看到占用内存最多的是DicomFileLike这种类型的对象.已经达到上万个, 这是不能忍受的.

就目前来看可能会有上边说的两种内存泄露原因导致不能回收这个对象.打开

pyrasite-shellpyrasite-shell

我先通过

gc.isenabled()

判断gc是否在工作, 结果发现是True, 也就是正常工作的, 而且使用gc.setdebug(gc.STATUS)设置gc为debug模式, 然后gc.collect()进行垃圾回收发现并没有更多内存释放,则否认了第二种泄露的可能.

现在来看gc.garbage中不能被释放的对象, 让我来检查一下是否有全局变量指向它们(这里极有可能是一个列表或者是一个字典)

gc.garbage 可以看到被塞满了各种DicomFileLike对象

记一次调试python内存泄露的问题解决方案分享

所以我们的目的就是先找到一个对象然后一级一级的向上寻找相互的引用.

记一次调试python内存泄露的问题解决方案分享

到这里发现其实没有更多的全局变量指向这个d了, 而且发现所以有的方法的对象地址和d是相同的, 说明了这个对象其实是自循环引用的.

那么python不可能不支持循环引用对象的回收吧? 跟着这个问题我查了一下stackoverflow

Does Python GC deal with reference-cycles like this?

这个问题的第一个回答介绍的很清楚了, 如果用户不自定类的__del__方法, gc可以回收带有自引用的对象, 但是你自己实现了__del__方法就不行了.

这就是python内存泄露的第三个可能.

回头看DicomFileLike的源码, 果然在__init__函数上方定义了一个__del__函数, 我这里使用了一个猴子补丁删除了这个方法, 内存泄露的问题就得以解决了.

总结

到这里整个调试过程就结束了, 然而实际上过程中做了很多曲折的工作, 在pyrasite中会找到几个引用DicomFileLike对象的object, 比较不容易辨别, 最开始我以为是某个全局的对象引用的DicomFileLike, 比如是列表什么的, 后来发现其实是locals()和globals()字典, 如果使用pyrasite-memory-viewer保存下来的数据会发现有一个大列表指向所有没有回收的DicomFileLike对象, 捯饬半天发现其实是gc.garbage, 好囧, 曾让我一度怀疑是第一种泄露方式, 但是怎么找这个对象都没有找到. 其中还有几次看到线程达到140+, 后来发现其实和线程一点关系没有, 线程维持在这个数目上边很稳定.

在这个过程中用到的其他几个hack的技巧有:

查看进程的线程数量

根据对象的id/address动态获取对象

查看垃圾回收的日志


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编程语言
    +关注

    关注

    10

    文章

    1947

    浏览量

    34823
  • python
    +关注

    关注

    56

    文章

    4800

    浏览量

    84821

原文标题:记一次调试python内存泄露的问题

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Triton编译器的常见问题解决方案

    Triton编译器作为款专注于深度学习的高性能GPU编程工具,在使用过程中可能会遇到些常见问题。以下是些常见问题的解决方案、安装
    的头像 发表于 12-24 18:04 520次阅读

    【电磁兼容技术案例分享】屏蔽罩结构缝隙导致辐射发射问题解决案例

    【电磁兼容技术案例分享】屏蔽罩结构缝隙导致辐射发射问题解决案例
    的头像 发表于 11-07 08:03 231次阅读
    【电磁兼容技术案例分享】屏蔽罩结构缝隙导致辐射发射<b class='flag-5'>问题解决</b>案例

    【电磁兼容技术案例分享】某氢燃料电池系统自兼容问题解决案例

    【电磁兼容技术案例分享】某氢燃料电池系统自兼容问题解决案例
    的头像 发表于 10-19 08:05 211次阅读
    【电磁兼容技术案例分享】某氢燃料电池系统自兼容<b class='flag-5'>问题解决</b>案例

    MLCC断裂问题解决方案

    MLCC断裂问题解决方案
    的头像 发表于 10-16 09:43 528次阅读

    C2000 F28004x系列MCU PLL锁相失败问题解决方案

    电子发烧友网站提供《C2000 F28004x系列MCU PLL锁相失败问题解决方案.pdf》资料免费下载
    发表于 09-27 11:24 0次下载
    C2000 F28004x系列MCU PLL锁相失败<b class='flag-5'>问题解决方案</b>

    【电磁兼容技术案例分享】高低压电缆混合布线导致的传导问题解决案例

    【电磁兼容技术案例分享】高低压电缆混合布线导致的传导问题解决案例
    的头像 发表于 09-12 08:05 379次阅读
    【电磁兼容技术案例分享】高低压电缆混合布线导致的传导<b class='flag-5'>问题解决</b>案例

    【电磁兼容技术案例分享】对地电容接地点差异导致辐射发射问题解决案例

    【电磁兼容技术案例分享】对地电容接地点差异导致辐射发射问题解决案例
    的头像 发表于 08-30 12:30 280次阅读
    【电磁兼容技术案例分享】对地电容接地点差异导致辐射发射<b class='flag-5'>问题解决</b>案例

    EMC问题解决实战教学4——CS抗扰度问题解决

    EMC问题解决之实战教学SES”CS抗扰度测试中,工程师常常会面对信号干扰、复杂的噪声模型、差共模干扰判断困难以及测试设备和方法选择等挑战。这些挑战不仅影响了测试的准确性和可靠性,同时也增加了工程师
    的头像 发表于 06-04 08:17 1235次阅读
    EMC<b class='flag-5'>问题解决</b>实战教学4——CS抗扰度<b class='flag-5'>问题解决</b>!

    EMC问题解决实战教学2——传导发射问题解决

    EMC问题解决之实战教学SES”信号干扰、复杂的噪声模型、差共模干扰判断困难以及测试设备和方法选择都是工程师在传导发射测试中常遇到的挑战。这些困难不仅影响了测试的准确性和可靠性,同时也增加了工程师
    的头像 发表于 05-28 08:17 608次阅读
    EMC<b class='flag-5'>问题解决</b>实战教学2——传导发射<b class='flag-5'>问题解决</b>!

    EMC问题解决实战教学1——辐射发射问题解决

    EMC问题解决之实战教学SES”辐射测试是个充满挑战的领域,工程师常常需要面对多种设备和复杂的测试流程,例如在设备选择和测试方法方面遇到各种挑战。在庞大且复杂的辐射数据也增加了数据解读的难度
    的头像 发表于 05-26 08:17 428次阅读
    EMC<b class='flag-5'>问题解决</b>实战教学1——辐射发射<b class='flag-5'>问题解决</b>!

    【电磁兼容技术案例分享】智能门禁的ESD问题解决案例

    【电磁兼容技术案例分享】智能门禁的ESD问题解决案例
    的头像 发表于 04-19 08:16 382次阅读
    【电磁兼容技术案例分享】智能门禁的ESD<b class='flag-5'>问题解决</b>案例

    浅谈公网无信号区域远程抄表问题解决方案及产品选型

    浅谈公网无信号区域远程抄表问题解决方案及产品选型 张颖姣 安科瑞电气股份有限公司 上海嘉定 201801 摘要:随着计量自动化系统的逐步完善,电网全用户表码信息采集成为系统数据得以深化应用的重要
    的头像 发表于 02-20 15:34 585次阅读
    浅谈公网无信号区域远程抄表<b class='flag-5'>问题解决方案</b>及产品选型

    一次诡异的内存泄漏

    最近在补些基础知识,恰好涉及到了智能指针std::weak_ptr在解决std::shared_ptr时候循环引用的问题
    的头像 发表于 02-19 13:44 678次阅读
    <b class='flag-5'>记</b><b class='flag-5'>一次</b>诡异的<b class='flag-5'>内存</b>泄漏

    【电磁兼容技术案例分享】由SGMII通讯导致的辐射发射高频单支超标问题解决案例

    【电磁兼容技术案例分享】由SGMII通讯导致的辐射发射高频单支超标问题解决案例
    的头像 发表于 02-19 13:20 978次阅读
    【电磁兼容技术案例分享】由SGMII通讯导致的辐射发射高频单支超标<b class='flag-5'>问题解决</b>案例

    如何使用linux下gdb来调试python程序

    如何使用linux下gdb来调试python程序  在Linux下,可以使用GDB(GNU调试器)来调试Python程序。GDB是
    的头像 发表于 01-31 10:41 2667次阅读