总结python 三种常见的内存泄漏场景
作者:蒋乐兴 发布时间:2023-02-18 16:37:49
概要
不要以为 Python 有自动垃圾回收就不会内存泄漏,本着它有“垃圾回收”我有“垃圾代码”的精神,现在总结一下三种常见的内存泄漏场景。
无穷大导致内存泄漏
如果把内存泄漏定义成只申请不释放,那么借着 Python 中整数可以无穷大的这个特点,我们一行代码就可以完成内存泄漏了。
i = 1024 ** 1024 ** 1024
循环引用导致内存泄漏
引用记数器 是 Python 垃圾回收机制的基础,如果一个对象的引用数量不为 0 那么是不会被垃圾回收的,我们可以通过 sys.getrefcount 来得到给定对象的引用数量。
In [1]: import sys
In [2]: a = {'name':'tom','age':16}
In [3]: sys.getrefcount(a) # 由于 getrefcount 内部也会临时的引用 a 所以,使得计数器的值变成了 2 。
Out[3]: 2
In [4]: b = a
In [5]: sys.getrefcount(a)
Out[5]: 3
先来看一个循环引用的场景。
#!/usr/bin/evn python3
import sys
import time
import threading
class Person(object):
free_lock = threading.Condition()
def __init__(self, name: str = ""):
"""
Parameters
----------
name: str
姓名
best_friend: str
最要好的朋友名
"""
self._name = name
self._best_friend = None
@property
def best_friend(self, person: "Person"):
return self._best_friend
@best_friend.setter
def best_friend(self, friend: "Person"):
self._best_friend = friend
def __str__(self):
"""
"""
return self._name
def __del__(self):
"""
"""
self.free_lock.acquire()
print(f"{self._name} 要 GG 了,现在释放它的内存空间。")
sys.stderr.flush()
self.free_lock.release()
def mem_leak():
"""
循环引用导致内存泄漏
"""
zhang_san = Person(name='张三')
li_si = Person("李四")
# 构造出循环引用
# 李四的好友是张三
li_si.best_friend = zhang_san
# 张三的好友是李四
zhang_san.best_friend = li_si
if __name__ == "__main__":
for i in range(3):
time.sleep(0.01)
print(f"{i}")
mem_leak()
print("mem_leak 执行完成了.")
time.sleep(5)
运行效果。
python3 main.py
0
1
2
mem_leak 执行完成了.
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间
由于循环引用的存在,使得 mem_leak 函数就行执行完了其内部的局部变量引用计数器也不为 0 ,所以内存得不到及时的释放。释放这部分内存有两个途径 1、 被 Python 内部的循环检测机制发现了; 2、进程退出前的集中释放。
tracemalloc 可以在一定程序上帮我们发现问题,在此就不讲怎么用了,我们直接上解决方案。Python 为程序员提供了弱引用,通过这种方式可以不增加对象引用计数器的数值,这成为了我们打破循环引用的一种手段。
In [1]: import sys
In [2]: import weakref
In [3]: from main import Person
In [4]: tom = Person('tom')
In [5]: sys.getrefcount(tom)
Out[5]: 2
In [6]: p = weakref.ref(tom)
In [7]: sys.getrefcount(tom) # 弱引用不会增加计数器的值
Out[7]: 2
现在使用 weakref 技术来改造我们的代码。
#!/usr/bin/evn python3
import sys
import time
import weakref
import threading
class Person(object):
free_lock = threading.Condition()
def __init__(self, name: str = ""):
"""
Parameters
----------
name: str
姓名
best_friend: str
最要好的朋友名
"""
self._name = name
self._best_friend = None
@property
def best_friend(self, person: "Person"):
return self._best_friend
@best_friend.setter
def best_friend(self, friend: "Person"):
self._best_friend = weakref.ref(friend)
def __str__(self):
"""
"""
return self._name
def __del__(self):
"""
"""
self.free_lock.acquire()
print(f"{self._name} 要 GG 了,现在释放它的内存空间。")
sys.stderr.flush()
self.free_lock.release()
def mem_leak():
"""
循环引用导致内存泄漏
"""
zhang_san = Person(name='张三')
li_si = Person("李四")
# 构造出循环引用
# 李四的好友是张三
li_si.best_friend = zhang_san
# 张三的好友是李四
zhang_san.best_friend = li_si
if __name__ == "__main__":
for i in range(3):
time.sleep(0.01)
print(f"{i}")
mem_leak()
print("mem_leak 执行完成了.")
time.sleep(5)
运行效果。
python3 main.py
0
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
1
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
2
张三 要 GG 了,现在释放它的内存空间。
李四 要 GG 了,现在释放它的内存空间。
mem_leak 执行完成了.
可以看到现在一旦函数执行完成,其内部的局部变量的内存就会得到释放,非常的及时。
外面库导致内存泄漏
这种情况我也只遇到过一次,之前 mysql-connector-python 的内存泄漏,导致我的程序跑着跑着占用的内存就越来越大;最后我们返的 C 语言扩展禁用之后就没有问题了。
来源:https://www.sqlpy.com/blogs/41065003#_3


猜你喜欢
- 一个完整的程序离不开日志,无论是开发阶段,还是测试阶段,亦或程序运行阶段,都可以通过日志查看程序的运行情况,或是定位问题。下面是对 pyth
- 引言周末我和小明又开始了疯狂的考证学习,昨晚通过合法的手段获取了一套学习资料,却遇到了一个问题:一套完整的资料,被机构拆分成了162个wor
- 一、 了解postman1. 什么是postman?------ 软件测试用来做接口测试的工具。2. 如何下载postman--
- 在一行内声明CSS,对比下面两个:h2 {font-size:18px; border:1px solid&n
- 由于DOM(文档对象模型)概念的推出,这个API使HTML如虎添翼,但是有些学DHTML的朋友还是有些困挠,只是因为目前的手册的书写不太科学
- 1、replicate_do_db 和 replicate_ignore_db 不要同时出现。容易出现混淆。也是毫无意义的。 Replica
- 内容摘要:严格地说,ASP 并不是一门编程语言,所以不存在类这一概念,我们这里说 ASP 类是指 A
- 摘要RepVgg通过结构重参数化让VGG再次伟大。 所谓“VGG式”指的是:没有任何分支结构。即通常
- 一直以来都对编译器和解析器有着很大的兴趣,也很清楚一个编译器的概念和整体的框架,但是对于细节部分却不是很了解。我们编写的程序源代码实际上就是
- 如何侦测HTTP表头信息?可用下列办法侦测并显示所有的HTTP HEADERS:<HTML><HEAD><TI
- 1.可以通过settings/dev.py的ALLOWED_HOSTS,设置允许访问# 设置哪些客户端可以通过地址访问到后端 A
- Python Queue模块Python中,队列是线程间最常用的交换数据的形式。Queue模块是提供队列操作的模块,虽然简单易用,但是不小心
- 这篇文章主要介绍了Python线程条件变量Condition原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习
- 本文实例讲述了MySql采用GROUP_CONCAT合并多条数据显示的方法,分享给大家供大家参考。具体实现方法如下:假设有这样一个需求:1:
- 项目场景:postman通常需要传递各式各样的参数,这样的话,进行写参数比较头疼,不知怎么进行传参。解决方案:可以考虑将参数对象写成json
- 本文实例为大家分享了Python/C++实现字符串逆序的具体代码,供大家参考,具体内容如下题目描述:将字符串逆序输出Python实现一:借助
- 项目发布版本会遇到经常需要清理缓存的问题,以下是项目禁用缓存的实际方法1.public文件夹中修改 index.html文件meta配置 &
- 目录前言一、首先二、接下来1.对照人脸获取2. 通过算法建立对照模型3.识别前言今天,我们用Python实现简单的人脸识别技术!Python
- 本文实例讲述了python实现的接收邮件功能。分享给大家供大家参考,具体如下:一 简介本代码实现从网易POP3服务器接收邮件二 代码impo
- 这两天接触了onmouseover事件和onmouseout事件,一直以为它们只是简单的分别实现鼠标指针移动到元素上时触发事件和在鼠标指针移