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

    文章

    1959

    浏览量

    38903
  • python
    +关注

    关注

    57

    文章

    4857

    浏览量

    89572

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

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

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

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    WebGL/Canvas 内存泄露分析

    在构建高性能、长周期运行的 WebGL/Canvas 应用(如 3D 编辑器、数据可视化平台)时,内存管理是个至关重要且极具挑战性的课题。 开发者通常面临的内存泄漏问题,其根源远比简单
    的头像 发表于 10-21 11:40 167次阅读
    WebGL/Canvas <b class='flag-5'>内存</b><b class='flag-5'>泄露</b>分析

    termux调试python猜数字游戏

    用termux做个猜数字游戏 下面是在Termux中创建猜数字游戏的步骤及完整实现方案,结合Python实现(最适配Termux环境): ? 、环境准备(Termux基础
    发表于 08-29 17:15

    伺服控制器干扰问题解决方案

    工程实践案例,系统分析干扰源类型及传播路径,并提出多层级解决方案、干扰源识别与影响分析 1. 传导干扰   电源线引入的高频噪声(如变频器回馈电流)通过共模方式侵入控制器,某汽车生产线曾因变频器与伺服系统共用
    的头像 发表于 07-31 18:13 1097次阅读
    伺服控制器干扰<b class='flag-5'>问题解决方案</b>

    小型低功耗一次电池解决方案

    近年来,物联网设备和可穿戴设备的快速普及,使得对续航时间更长、体积更小的原电池供电设备的需求日益增长。特瑞仕提供针对搭载原电池的设备优化的电源IC解决方案
    的头像 发表于 07-02 09:20 682次阅读
    小型低功耗<b class='flag-5'>一次</b>电池<b class='flag-5'>解决方案</b>

    一次消谐装置与二消谐装置区别、一次消谐器与二消谐器的区别

    一次消谐器与二消谐器是电力系统中用于抑制谐振过电压的不同装置,主要区别如下: 安装位置:一次消谐器串联于电压互感器(PT)一次侧中性点与地之间,直接承受高电压;二
    的头像 发表于 05-07 09:58 3195次阅读
    <b class='flag-5'>一次</b>消谐装置与二<b class='flag-5'>次</b>消谐装置区别、<b class='flag-5'>一次</b>消谐器与二<b class='flag-5'>次</b>消谐器的区别

    Python从入门到精通背手册

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

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

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

    是德科技正式发布LPDDR6设计与测试解决方案

    是德科技近日宣布,正式推出针对LPDDR6(第六代低功耗双倍数据速率内存标准)的完整设计和测试解决方案,旨在助力内存系统技术创新的新轮浪潮。这
    的头像 发表于 02-12 14:12 1003次阅读

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

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

    Python Connector for InterBase连接解决方案

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

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

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

    SFP光模块的兼容性问题解决方案

    模块在市场上得到了广泛的应用。然而,在实际使用过程中,用户可能会遇到些兼容性问题,这些问题可能会影响到通信系统的正常运行。以下是针对SFP光模块兼容性问题的解决方案: 1. 了解SFP光模块的规格和标准 在购买和使用SFP
    的头像 发表于 01-17 09:13 1985次阅读

    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 6385次阅读

    mac的常见问题解决方法

    Mac常见问题解决方法 1. 系统启动缓慢 问题描述: 启动Mac时,系统启动缓慢,甚至出现卡顿现象。 解决方法: 检查启动项目: 打开系统偏好设置中的“用户与群组”,点击“登录项”,移除不必要
    的头像 发表于 12-19 15:02 1849次阅读