总结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基于socket实现网络广播的方法
Apr 29 Python
python实现域名系统(DNS)正向查询的方法
Apr 19 Python
centos6.7安装python2.7.11的具体方法
Jan 16 Python
Python模块结构与布局操作方法实例分析
Jul 24 Python
python 通过字符串调用对象属性或方法的实例讲解
Apr 21 Python
Python视频爬虫实现下载头条视频功能示例
May 07 Python
Python wxpython模块响应鼠标拖动事件操作示例
Aug 23 Python
Python的matplotlib绘图如何修改背景颜色的实现
Jul 16 Python
python中的Elasticsearch操作汇总
Oct 30 Python
pytorch实现CNN卷积神经网络
Feb 19 Python
为什么python比较流行
Jun 19 Python
Python中tkinter的用户登录管理的实现
Apr 22 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+Apache环境中如何隐藏Apache版本
2017/11/24 PHP
laravel框架中控制器的创建和使用方法分析
2019/11/23 PHP
Laravel5.5+ 使用API Resources快速输出自定义JSON方法详解
2020/04/06 PHP
jquery如何实现锚点链接之间的平滑滚动
2013/12/02 Javascript
javascript内置对象arguments详解
2014/03/16 Javascript
JavaScript框架(iframe)操作总结
2014/04/16 Javascript
js的[defer]和[async]属性
2014/11/24 Javascript
JavaScript实现的双向跨域插件分享
2015/01/31 Javascript
asp.net+js实现金额格式化
2015/02/27 Javascript
JavaScript 事件对象介绍
2015/04/13 Javascript
JavaScript数据库TaffyDB用法实例分析
2015/07/27 Javascript
深入理解jquery自定义动画animate()
2016/05/24 Javascript
微信小程序 Button 组件详解及简单实例
2017/01/10 Javascript
JS实现经典的中国地区三级联动下拉菜单功能实例【测试可用】
2017/06/06 Javascript
vue音乐播放器插件vue-aplayer的配置及其使用实例详解
2017/07/10 Javascript
基于JavaScript实现弹幕特效
2020/08/27 Javascript
浅谈Vue父子组件和非父子组件传值问题
2017/08/22 Javascript
vue 实现图片懒加载功能
2020/12/31 Vue.js
[05:31]DOTA2英雄梦之声_第04期_光之守卫
2014/06/23 DOTA
[46:48]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第三局
2016/02/25 DOTA
[47:20]DAC2018 4.4 淘汰赛 Optic vs Mineski 第一场
2018/04/05 DOTA
python利用dir函数查看类中所有成员函数示例代码
2017/09/08 Python
Python Pywavelet 小波阈值实例
2019/01/09 Python
Python+opencv+pyaudio实现带声音屏幕录制
2019/12/23 Python
CSS3中border-radius属性设定圆角的使用技巧
2016/05/10 HTML / CSS
浅析css3中matrix函数的使用
2016/06/06 HTML / CSS
详解css3 flex弹性盒自动铺满写法
2020/09/17 HTML / CSS
如果NULL和0作为空指针常数是等价的,那我到底该用哪一个
2014/09/16 面试题
体育教师个人的自我评价
2014/02/16 职场文书
售后服务承诺书范文
2014/03/26 职场文书
计算机毕业生自荐信
2014/06/12 职场文书
诚实守信演讲稿
2014/09/01 职场文书
党员演讲稿
2014/09/04 职场文书
中学生清明节演讲稿
2015/03/18 职场文书
2016年大学迎新晚会工作总结
2015/10/15 职场文书
日元符号 ¥
2022/02/17 杂记