python内存泄漏浅析

news2025/7/19 10:17:44

一、概述

以前没有对内存泄漏有过相关的排查手段,一般个人使用python写的程序,不是那种长时间运行的程序,很少会去注意内存是否出现泄漏,但是如果程序是作为服务器的服务,需要长时间运行的,即使是很小的内存泄漏,最后也会特别明显。

对于 python 这种支持垃圾回收的语言来说,怎么还会有内存泄露? 概括来说,
有以下三种原因:

1、所用到的用 C 语言开发的底层模块中出现了内存泄露。
2、代码中用到了全局的 list、 dict 或其它容器,不停的往这些容器中插入对象,
而忘记了在使用完之后进行删除回收
3、代码中有“引用循环”,并且被循环引用的对象定义了del方法,就会发生内存泄
露。垃圾回收具体原理参见:Python的垃圾回收机制(引用计数) - 心系天下1 - 博客园

推荐三种调试Python内存泄漏利器:
memory-profiler、filprofiler、objgraph (https://mg.pov.lt/objgraph/)

二、现象

怎么判断是否存现内存泄漏呢?

简单的方法就是查看 程序占用的内存,从开始运行时记录占用的内存,持续观察一段时间,看内存是否持续在上升。

如linux系统中,可以使用top指令,查看某个进程RES值

持续一两天观察,某个进程的RES内存占用是否持续升高。如果是docker的话,可以使用

docker  stats  容器id 来查看当前容器的内存占用情况

 三、调试

如果说已经确认是内存持续增长,那么我们就需要在python程序中加上一些日志或者利用上面说的工具来确认到底在哪里发生了泄漏,才能针对性的解决。

1、objgraph   具体使用参见官网:https://mg.pov.lt/objgraph/

比较常用的函数:

def count(typename)

  返回该类型对象的数目,其实就是通过gc.get_objects()拿到所用的对象,然后统计指定类型的数目。

def by_type(typename)

  返回该类型的对象列表。线上项目,可以用这个函数很方便找到一个单例对象

def show_most_common_types(limits = 10)

  打印实例最多的前N(limits)个对象,这个函数非常有用。在《Python内存优化》一文中也提到,该函数能发现可以用slots进行内存优化的对象

def show_growth()

  统计自上次调用以来增加得最多的对象,这个函数非常有利于发现潜在的内存泄露。函数内部调用了gc.collect(),因此即使有循环引用也不会对判断造成影响。

def show_backrefs()

  生产一张有关objs的引用图,看出看出对象为什么不释放。

  该API有很多有用的参数,比如层数限制(max_depth)、宽度限制(too_many)、输出格式控制(filename output)、节点过滤(filter, extra_ignore),建议使用之间看一些document。

def find_backref_chain(obj, predicate, max_depth=20, extra_ignore=()):

  找到一条指向obj对象的最短路径,且路径的头部节点需要满足predicate函数 (返回值为True)

  可以快捷、清晰指出 对象的被引用的情况

def show_chain():

  将find_backref_chain 找到的路径画出来, 该函数事实上调用show_backrefs,只是排除了所有不在路径中的节点。

这里使用def show_growth() 来看下到底哪些类型的对象发生率泄漏,show_growth()

limit参数表示打印出最增加最多的对象的数量,file参数可不填写,不写直接在终端界面打印。

show_most_common_types()    打印实例最多的前N(limits)个对象

 在程序最后加上以下代码,看下每次执行后的变化

 import  objgraph      
 curr_dir = os.path.dirname(os.path.abspath(__file__))
 file_name = curr_dir + os.sep + 'objgraph.txt'
 with open(file_name, "a", encoding='utf-8') as can:
      can.write("-----------show_growth----------------\n")
      objgraph.show_growth(limit=8, file=can)
      can.write("----------show_most_common_types--------------\n")
      objgraph.show_most_common_types(limit=8, file=can)

效果:

这里第一次执行,增加了较多的对象是正常的。主要看增量,长时间下来哪些类型的变量是持续增加的。

 除了上面说的调试手段外,可以加一些日志去判断。比如,在有使用到多线程的编程中,怀疑是python多线程使用后未关闭,可以在程序最后加上线程数的打印


        logger.debug(f"线程总数: {len(threading.enumerate())}")
        logger.debug(f"threading.activeCount(): {threading.activeCount()}")

持续一段时间后,如果是线程数量持续增加,那基本可以判定有线程泄漏

线程关闭可以参考:python线程关闭_0流云0的博客-CSDN博客

一种是使用 ctypes强制杀死的方法,这种方法有时候在linux中会失败,暂时没搞清楚原理。

另一种使用在线程函数中通过信号使用break的方法退出线程。

看是否线程关闭,除了上面的数量外,可以使用is_alive() 函数判断当前线程的状态,为False表示关闭成功。

    status = my_thread.is_alive()
    # my_thread是threading.Thread创建的线程对象
    logger.info(f'线程状态: {status}')

调用线程关闭后,最好等待一两秒才获取线程状态,因为有时候状态更新会有点慢。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37873.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

毕业设计-基于机器视觉的口罩佩戴检测识别

目录 前言 课题背景和意义 实现技术思路 数据来源 COCO数据集预训练模型 图片检测 视频检测 训练&评估结果 实现效果图样例 前言 📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近…

结冰过程渲染-Ovito实现

结冰过程渲染-Ovito实现结冰过程渲染后结果一、渲染步骤-主要突出内容二、识别并区分冰晶和溶液三、渲染溶液中的水四、渲染出溶液中的冰五、突出溶液中溶质、金属板的显示六、data测试文件下载结冰过程渲染后结果 一、渲染步骤-主要突出内容 这里我们主要研究掺杂溶质如何影响…

k8s网络插件之Flannel

Flannel简介 Flannel官网:https://github.com/coreos/flannel Flannel是由CoreOS开源的针对k8s的网络服务,其目的是为解决k8s集群中各主机上Pod之间的通信问题,其借助etcd维护网络IP地址分配,并为每个Node节点分配一个不同的IP地…

学生HTML个人网页作业作品 HTML+CSS校园环保(大学生环保网页设计与实现)

🎀 精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

redis : 持久化

redis通过将数据放在内存里实现高速访问,为了防止意外情况,其数据也可以存放起来 持久化的实现方式有两种方案:一种是直接保存当前已经存储的数据,相当于复制内存中的数据到硬盘上,需要恢复数据时直接读取即可&#x…

代理模式与动态代理深入理解

一,代理模式的简单认识 1.参与者: 代理对象,被代理对象 代理对象相当于现实生活中的房产中介,被代理对象 相当于 房东 2.目的:保护被代理对象 避免外界直接修改被代理对象,破坏掉被代理对象原本的功能。…

KubeGems容器云平台体验

KubeGems容器云平台体验 KubeGems 是一款开源的企业级多租户容器云平台。围绕云原生社区,KubeGems 提供了多 Kubernetes 集群接入能力,并具备丰富的组件管理和资源成本分析功能,能够帮助企业快速的构建和打造一个本地化、功能强大且低成本的…

关于BigInteger和BigDecimal

BigInteger BigInteger类是用于解决整形类型(含基本数据类型及对应的包装类,)无法表示特别大的数字及运算的问题,即使是占用字节数最多的整形long,能表示的范围也是有限的.理论上,你可以使用BigInteger表示任意整数基于java8中BigInteger的构造方法. BigDecimal的构造方法2 …

Java代码审计基础——RMI原理和反序列化利用链

目录 (一)何为RMI (二)、 RMI的模式与交互过程 0x01 设计模式 0x02 交互过程 0x03 Stub和Skeleton (三)简单的 RMI Demo 1、Server 2、Registry 3、Client 补充——动态类加载机制 几个函数 (…

Java集合框架详解(四)——Map接口、HashMap类、LinkedHashMap类

一、Map接口 Map接口的特点: (1)映射键值对的形式(key和value); (2)Map集合中,key是不能重复的,value是可以重复的; (3)…

解决报错:fatal: Authentication failed for ‘https://github.com/*/*.git/‘

目录 问题 解决 步骤一、 步骤二、 步骤三、 ​步骤四、 ​步骤五、 步骤六、 问题 今天创建一个 github 新仓库,首次上传本地代码时,遇到了一个报错。但是,之前这样操作肯定是没有问题的,毕竟我可以保证用户名和密码都是…

复杂环境下多移动机器人路径规划研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab仿真内容点击👇 智能优化算法 …

数据结构 | 顺序栈与链式队【栈与队列的交际舞】

数据结构之栈与队列🌳顺序栈🍃前言🔥栈的结构简介及概述🔥为什么要用顺序栈?🔥结构声明🍃接口算法实现🍞初始化栈🍞销毁栈🍞入栈🍞出栈&#x1f3…

磨金石教育|干货分享:剪辑技法之跳切(上)

有一种剪辑手法划分了传统剪辑与现代剪辑的界限,它就是“跳切”; 跳切,是“切”的一种。属于一种无技巧的剪辑手法。它打破常规状态镜头切换时所遵循的时空和动作连续性要求,以较大幅度的跳跃式镜头组接,突出某些必要内…

【kafka】三、kafka命令行操作

kafka命令行操作 kafka的相关操作命令脚本文件在bin目录下 查看所有的topic kafka-topics.sh --zookeeper hll1:2181 --list 或 kafka-topics.sh --zookeeper 192.168.171.132:2181 --listkafka-topics.sh:topic执行脚本 --zookeeper hll1:2181:需要的…

[carla]把carla世界坐标系 转换为 俯视地图像素坐标系

在下面这篇参考博客中介绍了如何手动获取从carla世界坐标系到俯视地图像素坐标系的旋转平移矩阵.我也是采用了一样的思路和代码,这里把实现的过程以及最后所有地图的变换矩阵记录如下. 参考博客:carla真实世界坐标系与全局俯视地图像素坐标系变换 文章目录代码:1.carla世界坐标…

【表白】html表白代码

目录一.引言二.表白效果展示1.惊喜表白2.烟花表白3.玫瑰花表白4.心形表白5.心加文字6.炫酷的特效一.引言 我们可以用一下好看的网页来表白,下面就有我觉得很有趣的表白代码 下载整套表白文件 二.表白效果展示 1.惊喜表白 2.烟花表白 源码:新建一个文本文…

基于51单片机的温度控制系统数码管显示蜂鸣器报警proteus仿真原理图PCB

功能: 0.本系统采用STC89C52作为单片机 1.系统实时监测并显示当前温度,并通过四位数码管显示 2.超过设定阈值,蜂鸣器将报警,同时控制相应继电器实现降温或者加热 3.系统具备三个功能按键,可更改温度上限和下限 4.采用D…

SpringBoot+Mybatis-Plus+Thymeleaf 实现增删改查+登录/注册

SQL -- student_info create table if not exists student_info ( sid int not null auto_increment comment 学生表主键 primary key, sname varchar(20) not null comment 学生账号登录名、姓名, pwd varchar(32) not null comment 密码, sex varchar(20) not null comment …

AQS源码解析 7.共享模式_CyclicBarrier重复屏障

AQS源码解析 —共享模式_CyclicBarrier重复屏障 简介 CyclicBarrier:循环屏障、循环栅栏,用来进行线程协作,等待线程满足某个计数。构造时设置『计数个数』,每个线程执行到某个需要“同步”的时刻调用 await() 方法进行等待&…