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

    文章

    1957

    浏览量

    36767
  • python
    +关注

    关注

    56

    文章

    4828

    浏览量

    87103

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

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

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Python从入门到精通背手册

    电子发烧友网站提供《Python从入门到精通背手册.pdf》资料免费下载
    发表于 03-28 17:43 ?51次下载

    欧度MEDI-SNAP一次性医用插头发布

    欧度MEDI-SNAP一次性医用插头产品组加入新成员啦!为满足一次性内窥镜、一次性手术消融刀等设备中的耗材需求,欧度将ODU MEDI-SNAP一次性医用插头的锁定方式扩展为插拔自锁和
    的头像 发表于 02-21 16:00 ?524次阅读

    【电磁兼容技术案例分享】USB屏蔽电缆接头搭接导致的BCI问题解决案例

    【电磁兼容技术案例分享】USB屏蔽电缆接头搭接导致的BCI问题解决案例
    的头像 发表于 02-11 09:41 ?610次阅读
    【电磁兼容技术案例分享】USB屏蔽电缆接头搭接导致的BCI<b class='flag-5'>问题解决</b>案例

    Python Connector for InterBase连接解决方案

    适用于 InterBase 的 Python 连接器 Python Connector for InterBase 是种可靠的连接解决方案,用于从
    的头像 发表于 01-22 14:34 ?474次阅读

    超声波焊接常见问题解决方案

    超声波焊接常见问题解决方案 1. 焊接不牢固 **问题描述:**焊接后的塑料部件强度不足,容易断裂。 解决方案: **检查焊接参数:**确保焊接时间、压力和振幅设置正确。 **清洁焊接面:**去除
    的头像 发表于 01-19 11:07 ?857次阅读

    EE-175: 仿真器与EZ-KIT Lite评估系统问题解决指南

    电子发烧友网站提供《EE-175: 仿真器与EZ-KIT Lite评估系统问题解决指南.pdf》资料免费下载
    发表于 01-08 14:41 ?0次下载
    EE-175: 仿真器与EZ-KIT Lite评估系统<b class='flag-5'>问题解决</b>指南

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

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

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

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

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

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

    MLCC断裂问题解决方案

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

    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 ?730次阅读
    【电磁兼容技术案例分享】高低压电缆混合布线导致的传导<b class='flag-5'>问题解决</b>案例

    ODU MEDI-SNAP一次性医用插拔自锁插头产品介绍

    为满足一次性内窥镜、一次性手术消融刀等设备中的耗材需求,欧度全新推出了MEDI-SNAP一次性医用插拔自锁插头,为医疗客户打造了组在品质与经济性上均能满足需求的高性价比
    的头像 发表于 09-10 09:59 ?893次阅读

    RT-Thread操作系统内存泄露,很快找到泄露者了

    刚好碰到内存泄露问题,但不知道是谁泄露了,翻出之前调试用过的代码改了下,很快找到
    发表于 09-01 11:14

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

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