总结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中使用百度音乐搜索的api下载指定歌曲的lrc歌词
Jul 18 Python
探究python中open函数的使用
Mar 01 Python
python使用matplotlib绘制柱状图教程
Feb 08 Python
PyQt5每天必学之关闭窗口
Apr 19 Python
Python 确定多项式拟合/回归的阶数实例
Dec 29 Python
浅析Python 实现一个自动化翻译和替换的工具
Apr 14 Python
django之状态保持-使用redis存储session的例子
Jul 28 Python
NumPy中的维度Axis详解
Nov 26 Python
python实现从ftp服务器下载文件
Mar 03 Python
Django中从mysql数据库中获取数据传到echarts方式
Apr 07 Python
Python通过Pillow实现图片对比
Apr 29 Python
Python字符串三种格式化输出
Sep 17 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命令行(cli)下执行PHP脚本文件的相对路径的问题解决方法
2015/05/25 PHP
PHP用PDO如何封装简单易用的DB类详解
2017/07/30 PHP
jquery图片上下tab切换效果
2011/03/18 Javascript
JS基础之undefined与null的区别分析
2011/08/08 Javascript
jquery.form.js用法之清空form的方法
2014/03/07 Javascript
解决angular的$http.post()提交数据时后台接收不到参数值问题的方法
2015/12/10 Javascript
自定义Angular指令与jQuery实现的Bootstrap风格数据双向绑定的单选与多选下拉框
2015/12/12 Javascript
js实现网页收藏功能
2015/12/17 Javascript
AngularJS表单验证中级篇(3)
2016/09/28 Javascript
微信小程序 页面跳转传参详解
2016/10/28 Javascript
js学习总结之dom2级事件基础知识详解
2017/07/27 Javascript
利用es6 new.target来对模拟抽象类的方法
2019/05/10 Javascript
vue-router源码之history类的浅析
2019/05/21 Javascript
优雅的使用javascript递归画一棵结构树示例代码
2019/09/22 Javascript
js实现随机点名程序
2020/09/17 Javascript
javascript 数组精简技巧小结
2020/02/26 Javascript
解决Tensorflow使用pip安装后没有model目录的问题
2018/06/13 Python
python读取一个目录下所有txt里面的内容方法
2018/06/23 Python
Python基础学习之类与实例基本用法与注意事项详解
2019/06/17 Python
在django-xadmin中APScheduler的启动初始化实例
2019/11/15 Python
Python自带的IDE在哪里
2020/07/01 Python
Python pickle模块常用方法代码实例
2020/10/10 Python
Python 删除List元素的三种方法remove、pop、del
2020/11/16 Python
python 基于wx实现音乐播放
2020/11/24 Python
英国玛莎百货新西兰:Marks & Spencer New Zealand
2019/07/21 全球购物
预备党员入党自我评价范文
2014/03/10 职场文书
交警个人先进事迹材料
2014/05/11 职场文书
安全宣传标语口号
2014/06/06 职场文书
公司股份合作协议书
2014/12/07 职场文书
食品药品安全责任书
2015/05/11 职场文书
辛亥革命观后感
2015/06/02 职场文书
致我们终将逝去的青春观后感
2015/06/10 职场文书
Java并发编程之详解CyclicBarrier线程同步
2021/06/23 Java/Android
Android基础入门之dataBinding的简单使用教程
2022/06/21 Java/Android
错误码NET::ERR_CERT_DATE_INVALID证书已过期解决方法?
2022/07/07 数码科技
Win10服务主机占用内存怎么办?Win10服务主机进程占用大量内存解决方法
2022/09/23 数码科技