总结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备份Mysql脚本
Aug 11 Python
一个检测OpenSSL心脏出血漏洞的Python脚本分享
Apr 10 Python
python和shell实现的校验IP地址合法性脚本分享
Oct 23 Python
python脚本监控docker容器
Apr 27 Python
python django 增删改查操作 数据库Mysql
Jul 27 Python
pygame游戏之旅 添加游戏界面按键图形
Nov 20 Python
详解pandas的外部数据导入与常用方法
May 01 Python
python+adb+monkey实现Rom稳定性测试详解
Apr 23 Python
Python如何实现邮件功能
May 27 Python
关于Python不换行输出和不换行输出end=““不显示的问题(亲测已解决)
Oct 27 Python
Python OpenCV实现传统图片格式与base64转换
Jun 13 Python
Python+OpenCV实现在图像上绘制矩形
Mar 21 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
骨王战斗力在公会成员中排不进前五,却当选了会长,原因竟是这样
2020/03/02 日漫
新浪新闻小偷
2006/10/09 PHP
php函数的常用方法及注意之处小结
2011/07/10 PHP
php模板引擎技术简单实现
2016/03/15 PHP
php+Ajax处理xml与json格式数据的方法示例
2019/03/04 PHP
TP - 比RBAC更好的权限认证方式(Auth类认证)
2021/03/09 PHP
JavaScript 字符编码规则
2009/05/04 Javascript
jQuery 使用手册(三)
2009/09/23 Javascript
浅谈tudou土豆网首页图片延迟加载的效果
2010/06/23 Javascript
Bootstrap模仿起筷首页效果
2016/05/09 Javascript
javascript中JSON.parse()与eval()解析json的区别
2016/05/19 Javascript
CodeMirror js代码加亮使用总结
2017/03/25 Javascript
echarts饼图扇区添加点击事件的实例
2017/10/16 Javascript
JS实现放大镜效果
2020/09/21 Javascript
Vue SPA 首屏优化方案
2021/02/26 Vue.js
[02:16]DOTA2超级联赛专访Burning 逆袭需要抓住机会
2013/06/24 DOTA
[06:16]DOTA2守卫传承者——职业选手谈心路历程
2015/02/26 DOTA
python 实现网上商城,转账,存取款等功能的信用卡系统
2016/07/15 Python
Python有序字典简单实现方法示例
2017/09/28 Python
Tensorflow之Saver的用法详解
2018/04/23 Python
Python2.7环境Flask框架安装简明教程【已测试】
2018/07/13 Python
详解Python基础random模块随机数的生成
2019/03/23 Python
python+OpenCV实现车牌号码识别
2019/11/08 Python
Mysql数据库反向生成Django里面的models指令方式
2020/05/18 Python
django创建css文件夹的具体方法
2020/07/31 Python
python“静态”变量、实例变量与本地变量的声明示例
2020/11/13 Python
详解CSS3媒体查询响应式布局bootstrap 框架原理实战(推荐)
2020/11/16 HTML / CSS
浅析HTML5的WebSocket与服务器推送事件
2016/02/19 HTML / CSS
某/etc/fstab文件中的某行如下: /dev/had5 /mnt/dosdata msdos defaults,usrquota 1 2 请解释其含义
2013/09/18 面试题
生产经理的自我评价分享
2013/11/07 职场文书
材料专业毕业生求职信
2014/02/26 职场文书
土建专业大学生自荐信范文
2014/04/09 职场文书
融资租赁计划书
2014/04/29 职场文书
2015年化工厂工作总结
2015/05/04 职场文书
MySQL中几种插入和批量语句实例详解
2021/09/14 MySQL
python数据可视化使用pyfinance分析证券收益示例详解
2021/11/20 Python