总结python 三种常见的内存泄漏场景


Posted in Python onNovember 20, 2020

概要

不要以为 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 语言扩展禁用之后就没有问题了。

以上就是总结python 三种常见的内存泄漏场景的详细内容,更多关于python 内存泄漏的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python fileinput模块使用实例
May 28 Python
Python的Django框架中if标签的相关使用
Jul 15 Python
在Lighttpd服务器中运行Django应用的方法
Jul 22 Python
Python实现返回数组中第i小元素的方法示例
Dec 04 Python
详解python string类型 bytes类型 bytearray类型
Dec 16 Python
Python Numpy 数组的初始化和基本操作
Mar 13 Python
python用户管理系统
Mar 13 Python
基于python的多进程共享变量正确打开方式
Apr 28 Python
Python使用Windows API创建窗口示例【基于win32gui模块】
May 09 Python
flask框架配置mysql数据库操作详解
Nov 29 Python
基于django 的orm中非主键自增的实现方式
May 18 Python
Python 合并拼接字符串的方法
Jul 28 Python
Python偏函数实现原理及应用
Nov 20 #Python
python与idea的集成的实现
Nov 20 #Python
安装pyinstaller遇到的各种问题(小结)
Nov 20 #Python
python3 re返回形式总结
Nov 20 #Python
python 实现图片修复(可用于去水印)
Nov 19 #Python
python 删除系统中的文件(按时间,大小,扩展名)
Nov 19 #Python
Python并发爬虫常用实现方法解析
Nov 19 #Python
You might like
php过滤危险html代码
2008/08/18 PHP
Ajax+PHP边学边练 之五 图片处理
2009/12/03 PHP
PHP 创建标签云函数代码
2010/05/26 PHP
php多维数组去掉重复值示例分享
2014/03/02 PHP
使用PHP导出Redis数据到另一个Redis中的代码
2014/03/12 PHP
Symfony2使用Doctrine进行数据库查询方法实例总结
2016/03/18 PHP
js计算页面刷新的次数
2009/07/20 Javascript
JSON 和 JavaScript eval使用说明
2010/06/13 Javascript
用js实现输入提示(自动完成)的实例代码
2013/06/14 Javascript
node.js中的console.info方法使用说明
2014/12/09 Javascript
简单模拟node.js中require的加载机制
2016/10/27 Javascript
Bootstrap基本插件学习笔记之Popover提示框(19)
2016/12/08 Javascript
JavaScript实现格式化字符串函数String.format
2016/12/16 Javascript
react+redux的升级版todoList的实现
2017/12/18 Javascript
node简单实现一个更改头像功能的示例
2017/12/29 Javascript
Vue中如何实现proxy代理
2018/04/20 Javascript
Vue中axios拦截器如何单独配置token
2019/12/27 Javascript
vue中解决微信html5原生ios虚拟键返回不刷新问题
2020/10/20 Javascript
[02:32]DOTA2完美大师赛场馆静安体育中心观赛全攻略
2017/11/08 DOTA
[01:23:59]2018DOTA2亚洲邀请赛 4.1 小组赛 B组 VP vs Secret
2018/04/03 DOTA
[08:40]Navi Vs Newbee
2018/06/07 DOTA
分析在Python中何种情况下需要使用断言
2015/04/01 Python
详解Python使用tensorflow入门指南
2018/02/09 Python
Python中pip更新和三方插件安装说明
2018/07/08 Python
对python中类的继承与方法重写介绍
2019/01/20 Python
python实现列表的排序方法分享
2019/07/01 Python
Python面向对象之Web静态服务器
2019/09/03 Python
python处理excel绘制雷达图
2019/10/18 Python
解决启动django,浏览器显示“服务器拒绝访问”的问题
2020/05/13 Python
利用html5 canvas破解简单验证码及getImageData接口应用
2013/01/25 HTML / CSS
担保书格式及范文
2014/04/01 职场文书
财务人员担保书
2014/05/13 职场文书
文明倡议书
2015/01/19 职场文书
中学生学习保证书
2015/02/26 职场文书
Spring Cloud 中@FeignClient注解中的contextId属性详解
2021/09/25 Java/Android
漫画「古见同学有交流障碍症」第25卷封面公开
2022/03/21 日漫