总结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 相关文章推荐
可用于监控 mysql Master Slave 状态的python代码
Feb 10 Python
在Mac OS上部署Nginx和FastCGI以及Flask框架的教程
May 02 Python
详解Python中open()函数指定文件打开方式的用法
Jun 04 Python
Python中Iterator迭代器的使用杂谈
Jun 20 Python
python3大文件解压和基本操作
Dec 15 Python
Python科学计算包numpy用法实例详解
Feb 08 Python
基于Django与ajax之间的json传输方法
May 29 Python
Python英文文本分词(无空格)模块wordninja的使用实例
Feb 20 Python
Python 20行简单实现有道在线翻译的详解
May 15 Python
python实现PID算法及测试的例子
Aug 08 Python
如何在pycharm中安装第三方包
Oct 27 Python
python归并排序算法过程实例讲解
Nov 04 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使用正则过滤js脚本代码实例
2014/05/10 PHP
PHP几个实用自定义函数小结
2016/01/25 PHP
ThinkPHP 5 AJAX跨域请求头设置实现过程解析
2020/10/28 PHP
Javascript document.referrer判断访客来源网址
2020/05/15 Javascript
Javascript的常规数组和关联数组对比小结
2012/05/24 Javascript
javascript定时变换图片实例代码
2013/03/17 Javascript
利用cookie记住背景颜色示例代码
2013/11/04 Javascript
javascript原型链继承用法实例分析
2015/01/28 Javascript
使用JavaScript制作一个简单的计数器的方法
2015/07/07 Javascript
AngularJS指令详解及示例代码
2016/08/16 Javascript
详解vue数据渲染出现闪烁问题
2017/06/29 Javascript
vue父子组件的嵌套的示例代码
2017/09/08 Javascript
Vue-router的使用和出现空白页,路由对象属性详解
2018/09/03 Javascript
jQuery实现的简单歌词滚动功能示例
2019/01/07 jQuery
jQuery实现左右两个列表框的内容相互移动功能示例
2019/01/27 jQuery
使用JS来动态操作css的几种方法
2019/12/18 Javascript
[45:18]完美世界DOTA2联赛循环赛 PXG vs IO 第二场 11.06
2020/11/09 DOTA
python通过ElementTree操作XML获取结点读取属性美化XML
2013/12/02 Python
python使用正则搜索字符串或文件中的浮点数代码实例
2014/07/11 Python
详解Python字符串切片
2019/05/20 Python
Python爬虫运用正则表达式的方法和优缺点
2019/08/25 Python
jenkins配置python脚本定时任务过程图解
2019/10/29 Python
TensorFlow设置日志级别的几种方式小结
2020/02/04 Python
Python基于os.environ从windows获取环境变量
2020/06/09 Python
浅谈keras中loss与val_loss的关系
2020/06/22 Python
Python pytesseract验证码识别库用法解析
2020/06/29 Python
浅谈django不使用restframework自定义接口与使用的区别
2020/07/15 Python
体育系毕业生求职自荐信
2014/04/16 职场文书
三八红旗手先进事迹材料
2014/05/13 职场文书
介绍信样本
2015/01/31 职场文书
公积金贷款承诺书
2015/04/30 职场文书
刑事辩护词范文
2015/05/21 职场文书
实习报告范文之电话客服岗位
2019/07/26 职场文书
餐厅如何利用“营销策略”扭转亏本局面
2019/10/15 职场文书
用Java实现简单计算器功能
2021/07/21 Java/Android
分享Python异步爬取知乎热榜
2022/04/12 Python