总结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数据库的正确姿势
Feb 03 Python
python梯度下降法的简单示例
Aug 31 Python
python3实现点餐系统
Jan 24 Python
详解在Python中以绝对路径或者相对路径导入文件的方法
Aug 30 Python
python 公共方法汇总解析
Sep 16 Python
python list数据等间隔抽取并新建list存储的例子
Nov 27 Python
详解Python3中的 input() 函数
Mar 18 Python
keras训练曲线,混淆矩阵,CNN层输出可视化实例
Jun 15 Python
python获取百度热榜链接的实例方法
Aug 25 Python
降低python版本的操作方法
Sep 11 Python
如何使用scrapy中的ItemLoader提取数据
Sep 30 Python
Python万能模板案例之matplotlib绘制直方图的基本配置
Apr 13 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中获取文件扩展名的N种方法小结
2012/02/27 PHP
php 文件上传实例代码
2012/04/19 PHP
PHP中session变量的销毁
2014/02/27 PHP
php获得url参数中具有&的值的方法
2014/03/05 PHP
php自定义session示例分享
2014/04/22 PHP
php输出指定时间以前时间格式的方法
2015/03/21 PHP
浅谈PHP中JSON数据操作
2015/07/01 PHP
PHP实现权限管理功能示例
2017/09/22 PHP
IE8提示Invalid procedure call or argument 异常的解决方法
2012/09/30 Javascript
js this函数调用无需再次抓获id,name或标签名
2014/03/03 Javascript
javascript表单验证和Window详解
2014/12/11 Javascript
JQuery中extend的用法实例分析
2015/02/08 Javascript
javascript实现2016新年版日历
2016/01/25 Javascript
javascript 分号总结及详细介绍
2016/09/24 Javascript
connection reset by peer问题总结及解决方案
2016/10/21 Javascript
JavaScript实现前端实时搜索功能
2020/03/26 Javascript
Angular4学习笔记之根模块与Ng模块
2017/09/09 Javascript
JavaScript实现微信号随机切换代码
2018/03/09 Javascript
Python的加密模块md5、sha、crypt使用实例
2014/09/28 Python
深入理解Python中range和xrange的区别
2017/11/26 Python
详谈python中冒号与逗号的区别
2018/04/18 Python
python对文件目录的操作方法实例总结
2019/06/24 Python
Python3 解决读取中文文件txt编码的问题
2019/12/20 Python
Python实现桌面翻译工具【新手必学】
2020/02/12 Python
Django返回HTML文件的实现方法
2020/09/17 Python
Pycharm操作Git及GitHub的步骤详解
2020/10/27 Python
python接口自动化框架实战
2020/12/23 Python
英国和世界各地鲜花速递专家:Arena Flowers
2018/02/10 全球购物
文明教师事迹材料
2014/01/16 职场文书
年会主持词结束语
2014/03/27 职场文书
医院义诊活动总结
2014/07/04 职场文书
企业党的群众路线教育实践活动学习心得体会
2014/10/31 职场文书
汇报材料怎么写
2014/12/30 职场文书
在职证明范本
2015/06/15 职场文书
企业廉洁教育心得体会
2016/01/20 职场文书
postgresql中如何执行sql文件
2023/05/08 PostgreSQL