总结python 三种常见的内存泄漏场景

概要

不要以为 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 内存泄漏的资料请关注我们其它相关文章!

(0)

相关推荐

  • Python内存泄漏和内存溢出的解决方案

    一.内存泄漏 像Java程序一样,虽然Python本身也有垃圾回收的功能,但是同样也会产生内存泄漏的问题. 对于一个用 python 实现的,长期运行的后台服务进程来说,如果内存持续增长,那么很可能是有了"内存泄露". 1.内存泄露的原因 对于 python 这种支持垃圾回收的语言来说,怎么还会有内存泄露? 概括来说,有以下三种原因: 所用到的用 C 语言开发的底层模块中出现了内存泄露. 代码中用到了全局的 list. dict 或其它容器,不停的往这些容器中插入对象,而忘记了在使用完

  • 解决python中显示图片的plt.imshow plt.show()内存泄漏问题

    当要处理批量图片,且每张图片都要进行显示时,用plt.imshow() plt.show()会出现内存泄漏, 管理器中看到其中一个python进程的内存不断上涨,目前有找到解决方法 from matplotlib import pyplot as plt ... for...: ... plt.figure(figsize=IMAGE_SIZE) plt.imshow(image_np) plt.show() 补充知识:python在内存中读取base64图片 import base64 imp

  • 记一次python 内存泄漏问题及解决过程

    最近工作中慢慢开始用python协程相关的东西,所以用到了一些相关模块,如aiohttp, aiomysql, aioredis等,用的过程中也碰到的很多问题,这里整理了一次内存泄漏的问题 通常我们写python程序的时候也很少关注内存这个问题(当然可能我的能力还有待提升),可能写c和c++的朋友会更多的考虑这个问题,但是一旦我们的python程序出现了 内存泄漏的问题,也将是一件非常麻烦的事情了,而最近的一次代码中也碰到了这个问题,不过好在最后内存溢出不是我代码的问题,而是所用到的一个包出现了

  • Python的内存泄漏及gc模块的使用分析

    一般来说在 Python 中,为了解决内存泄漏问题,采用了对象引用计数,并基于引用计数实现自动垃圾回收. 由于Python 有了自动垃圾回收功能,就造成了不少初学者误认为自己从此过上了好日子,不必再受内存泄漏的骚扰了.但如果仔细查看一下Python文档对 __del__() 函数的描述,就知道这种好日子里也是有阴云的.下面摘抄一点文档内容如下: Some common situations that may prevent the reference count of an object fro

  • 对python程序内存泄漏调试的记录

    问题描述 调试python程序时,用下面这段代码,可以获得进程占用系统内存值.程序跑一段时间后,就能画出进程对内存的占用情况. def memory_usage_psutil(): # return the memory usage in MB import psutil,os process = psutil.Process(os.getpid()) mem = process.memory_info()[0] / float(2 ** 20) return mem 发现进程的内存占用一直再上

  • 粗略分析Python中的内存泄漏

    引子 之前一直盲目的认为 Python 不会存在内存泄露, 但是眼看着上线的项目随着运行时间的增长 而越来越大的内存占用, 我意识到我写的程序在发生内存泄露, 之前 debug 过 logging 模块导致的内存泄露. 目前看来, 还有别的地方引起的内存泄露. 经过一天的奋战, 终于找到了内存泄露的地方, 目前项目 跑了很长时间, 在业务量较小的时候内存还是能回到刚启动的时候的内存占用. 什么情况下不用这么麻烦 如果你的程序只是跑一下就退出大可不必大费周章的去查找是否有内存泄露, 因为 Pyth

  • 总结python 三种常见的内存泄漏场景

    概要 不要以为 Python 有自动垃圾回收就不会内存泄漏,本着它有"垃圾回收"我有"垃圾代码"的精神,现在总结一下三种常见的内存泄漏场景. 无穷大导致内存泄漏 如果把内存泄漏定义成只申请不释放,那么借着 Python 中整数可以无穷大的这个特点,我们一行代码就可以完成内存泄漏了. i = 1024 ** 1024 ** 1024 循环引用导致内存泄漏 引用记数器 是 Python 垃圾回收机制的基础,如果一个对象的引用数量不为 0 那么是不会被垃圾回收的,我们可以

  • js内存泄漏场景、如何监控及分析详解

    目录 前言 哪些情况会引起内存泄漏 1. 意外的全局变量 2. 遗忘的定时器 3. 使用不当的闭包 4. 遗漏的 DOM 元素 5. 网络回调 如何监控内存泄漏 如何分析内存泄漏,找出有问题的代码 实例分析 总结 前言 Q:什么是内存泄漏? 字面上的意思,申请的内存没有及时回收掉,被泄漏了 Q:为什么会发生内存泄漏? 虽然前端有垃圾回收机制,但当某块无用的内存,却无法被垃圾回收机制认为是垃圾时,也就发生内存泄漏了 而垃圾回收机制通常是使用标志清除策略,简单说,也就是引用从根节点开始是否可达来判定

  • Golang内存泄漏场景以及解决方案详析

    目录 1.字符串截取 2.切片截取引起子切片内存泄漏 3.没有重置丢失的子切片元素中的指针 4.函数数组传参 5.goroutine 6.定时器 1)time.After 2)timer.ticker 总结 1.字符串截取 func main() { var str0 = "12345678901234567890" str1 := str0[:10] } 以上代码,会有10字节的内存泄漏,我们知道,str0和str1底层共享内存,只要str1一直活跃,str0 就不会被回收,10字节

  • python 监控某个进程内存的情况问题

    目录 python监控某个进程内存 python监控进程并重启 分析了具体思路 相关代码很简单 python监控某个进程内存 测试场景: 某个客户端程序长时间运行后存在内存泄漏问题,现在开发解决了需要去验证这个问题是否还存在,并要求出具相应测试验证报告. 手段: 需要有一个工具能够实时去获取该程序进程一直运行下占用内存,CPU使用率情况. 方法: python去实现这么个监控功能 import sys import time import psutil sys.argv # get pid fr

  • 一篇文章弄懂javascript内存泄漏

    1.什么是内存泄漏 在了解什么是内存泄漏之前, 我们应该要对内存是什么有个概念, 随机存取存储器(英语:Random Access Memory,缩写:RAM)是与 CPU 直接交换数据的内部存储器.它可以随时读写, 而且速度很快,通常作为操作系统或其他正在运行中的程序的临时资料存储介质. 什么是内存泄漏? : 程序不再需要使用的内存, 但是又没有及时释放, 就叫做内存泄漏! 然后在理解泄漏之前, 我们的了解下内存的管理, 在一些底层语言中, 如C语言, 内存是需要开发者自己分配和释放的, 通过

  • C++之内存泄漏排查详解

    目录 一 .经验排查 二 .使用Visual Leak Detector for Visual C++ 2.1 Visual Leak Detector for Visual C++简介 2.2 Visual Leak Detector源码获取编译 2.2.1 源码获取,相关git地址 2.2.2 发布版本获取 2.2.3 进行编译 2.2.4 自带gtest工程测试 2.3 如何测试自己的项目呢 2.3.1 配置工程 2.3.2 编写简单的测试用例 2.3.3 检测结果如图 三.总结 一 .经

  • 手把手教你如何排查Javascript内存泄漏

    目录 引言 如何判断我的应用发生了内存泄漏 Performance和Memory都可以用来定位内存问题,先用谁呢 通过Memory面板定位内存泄漏的流程通常是怎么样的呢 为什么我的内存快照记录下来之后看不懂,还出现了很多奇怪的变量 快照里有一些“Detached DOM tree”,是什么意思 Shallow size 和 Retained size,它们有什么不同 Memory里的Summary视图, Comparison视图, Dominators视图和Containment视图分别有什么不

随机推荐