总结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中何种情况下需要使用断言
Apr 01 Python
Python 的内置字符串方法小结
Mar 15 Python
详解duck typing鸭子类型程序设计与Python的实现示例
Jun 03 Python
用python实现简单EXCEL数据统计的实例
Jan 24 Python
Python字典,函数,全局变量代码解析
Dec 18 Python
快速了解Python中的装饰器
Jan 11 Python
Python读写及备份oracle数据库操作示例
May 17 Python
Python 单元测试(unittest)的使用小结
Nov 14 Python
django迁移数据库错误问题解决
Jul 29 Python
Python FFT合成波形的实例
Dec 04 Python
Tensorflow tf.tile()的用法实例分析
May 22 Python
python3 通过 pybind11 使用Eigen加速代码的步骤详解
Dec 07 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中数组定义的几种方法
2013/09/01 PHP
显示youtube视频缩略图和Vimeo视频缩略图代码分享
2014/02/13 PHP
PHP常用操作类之通信数据封装类的实现
2017/07/16 PHP
PHP用函数嵌入网站访问量计数器
2017/10/27 PHP
PHP使用file_get_contents发送http请求功能简单示例
2018/04/29 PHP
NodeJs中的VM模块详解
2015/05/06 NodeJs
JQuery中DOM事件合成用法实例分析
2015/06/13 Javascript
文字垂直滚动之javascript代码
2015/07/29 Javascript
js友好的时间返回函数
2016/08/24 Javascript
微信小程序联网请求的轮播图
2017/07/07 Javascript
利用JQuery操作iframe父页面、子页面的元素和方法汇总
2017/09/10 jQuery
JS 实现分页打印功能
2018/05/16 Javascript
Vue.JS实现垂直方向展开、收缩不定高度模块的JS组件
2018/06/19 Javascript
Vue中的methods、watch、computed的区别
2018/11/26 Javascript
vue项目前端错误收集之sentry教程详解
2019/05/27 Javascript
vue input输入框关键字筛选检索列表数据展示
2020/10/26 Javascript
基于jQuery的时间戳与日期间的转化
2019/06/21 jQuery
Layui数据表格跳转到指定页的实现方法
2019/09/05 Javascript
Vue 开发必须知道的36个技巧(小结)
2019/10/09 Javascript
jQuery 实现扁平式小清新导航
2020/07/07 jQuery
关于Js中new操作符的作用详解
2021/02/21 Javascript
python 文件与目录操作
2008/12/24 Python
研究Python的ORM框架中的SQLAlchemy库的映射关系
2015/04/25 Python
Python利用带权重随机数解决抽奖和游戏爆装备问题
2016/06/16 Python
Python安装官方whl包和tar.gz包的方法(推荐)
2017/06/04 Python
python爬虫_实现校园网自动重连脚本的教程
2018/04/22 Python
opencv3/C++实现视频读取、视频写入
2019/12/11 Python
python 命名规范知识点汇总
2020/02/14 Python
Python中求对数方法总结
2020/03/10 Python
通信工程专业毕业生推荐信
2013/12/25 职场文书
行政助理的岗位职责
2014/02/18 职场文书
社区母亲节活动方案
2014/03/05 职场文书
大三学生学年自我鉴定
2014/09/12 职场文书
大学生自我鉴定怎么写
2019/05/07 职场文书
集英社今正式宣布 成立游戏公司“集英社Games”
2022/03/31 其他游戏
HTML页面中使两个div并排显示的实现
2022/05/15 HTML / CSS