总结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中使用成员运算符的示例
May 13 Python
Python下实现的RSA加密/解密及签名/验证功能示例
Jul 17 Python
分享6个隐藏的python功能
Dec 07 Python
Tensorflow 自带可视化Tensorboard使用方法(附项目代码)
Feb 10 Python
python构建深度神经网络(续)
Mar 10 Python
python 生成图形验证码的方法示例
Nov 11 Python
Python多进程写入同一文件的方法
Jan 14 Python
详解Python sys.argv使用方法
May 10 Python
浅谈Pycharm最有必要改的几个默认设置项
Feb 14 Python
Python发送手机动态验证码代码实例
Feb 28 Python
Python定义一个Actor任务
Jul 29 Python
Python面向对象编程之类的概念
Nov 01 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中strstr、strrchr、substr、stristr四个函数的区别总结
2014/09/22 PHP
PHP获取客户端及服务器端IP的封装类
2016/07/21 PHP
javascript web页面刷新的方法收集
2009/07/02 Javascript
jQuery源码分析-03构造jQuery对象-工具函数
2011/11/14 Javascript
JQuery入门——用one()方法绑定事件处理函数(仅触发一次)
2013/02/05 Javascript
13 款最热门的 jQuery 图像 360 度旋转插件推荐
2014/12/09 Javascript
JavaScript ES5标准中新增的Array方法
2016/06/28 Javascript
老生常谈 js中this的指向
2016/06/30 Javascript
关于两个jQuery(js)特效冲突的bug的解决办法
2016/09/04 Javascript
JavaScript trim 实现去除字符串首尾指定字符的简单方法
2016/12/27 Javascript
浅谈vue-router 路由传参的方法
2017/12/27 Javascript
.vue文件 加scoped 样式不起作用的解决方法
2018/05/28 Javascript
详解Webpack + ES6 最新环境搭建与配置
2018/06/04 Javascript
详解React中setState回调函数
2018/06/14 Javascript
微信小程序网络请求封装示例
2018/07/24 Javascript
详解npm 配置项registry修改为淘宝镜像
2018/09/07 Javascript
npm 常用命令详解(小结)
2019/01/17 Javascript
Vue路由之JWT身份认证的实现方法
2019/08/26 Javascript
原生js实现商品筛选功能
2019/10/28 Javascript
15分钟上手vue3.0(小结)
2020/05/20 Javascript
[45:46]2014 DOTA2国际邀请赛中国区预选赛5.21 HGT VS DT
2014/05/23 DOTA
Python常用随机数与随机字符串方法实例
2015/04/09 Python
使用Python的内建模块collections的教程
2015/04/28 Python
Python简单调用MySQL存储过程并获得返回值的方法
2015/07/20 Python
使用TensorFlow实现SVM
2018/09/06 Python
widows下安装pycurl并利用pycurl请求https地址的方法
2018/10/15 Python
使用python脚本自动创建pip.ini配置文件代码实例
2019/09/20 Python
Python实现图片识别加翻译功能
2019/12/26 Python
阿拉伯世界最大的电子商务网站:Souq沙特阿拉伯
2016/10/28 全球购物
java程序员面试交流
2012/11/29 面试题
入党自荐书范文
2015/03/05 职场文书
食品药品安全责任书
2015/05/11 职场文书
六一儿童节园长致辞
2015/07/31 职场文书
高三生物教学反思
2016/02/22 职场文书
MySQL连接控制插件介绍
2021/09/25 MySQL
解决Oracle数据库用户密码过期
2022/05/11 Oracle