【问题标题】:Is this a memory leak? (python + numpy)这是内存泄漏吗? (蟒蛇+麻木)
【发布时间】:2013-06-09 17:28:33
【问题描述】:

下面的代码填满了我的所有记忆:

from sys import getsizeof
import numpy

# from http://stackoverflow.com/a/2117379/272471
def getSize(array):
    return getsizeof(array) + len(array) * getsizeof(array[0])


class test():
    def __init__(self):
        pass
    def t(self):
        temp = numpy.zeros([200,100,100])
        A = numpy.zeros([200], dtype = numpy.float64)
        for i in range(200):
            A[i] = numpy.sum( temp[i].diagonal() ) 
        return A

a = test()
memory_usage("before")
c = [a.t() for i in range(100)]
del a
memory_usage("After")
print("Size of c:", float(getSize(c))/1000.0)

输出是:

('>', 'before', 'memory:', 20588, 'KiB  ')
('>', 'After', 'memory:', 1583456, 'KiB  ')
('Size of c:', 8.92)

如果 c 约为 9 KiB,为什么我要使用 ~1.5 GB 的内存?这是内存泄漏吗? (谢谢)

memory_usage 函数已发布在 SO 上,为清楚起见,在此报告:

def memory_usage(text = ''):
    """Memory usage of the current process in kilobytes."""
    status = None
    result = {'peak': 0, 'rss': 0}
    try:
        # This will only work on systems with a /proc file system
        # (like Linux).
        status = open('/proc/self/status')
        for line in status:
            parts = line.split()
            key = parts[0][2:-1].lower()
            if key in result:
                result[key] = int(parts[1])
    finally:
        if status is not None:
            status.close()
    print('>', text, 'memory:', result['rss'], 'KiB  ')
    return result['rss']

【问题讨论】:

  • float(getSize(c))/1000.0)替换float(getsizeof(c))/1000.0),你应该会得到更好的价值。
  • 感谢glglgl,我实际上复制了错误版本的示例代码。已更正。
  • 如果在 t() 中的 return 语句之前放入 del temp 会发生什么?
  • 不起作用。正如我所写,问题来自 numpy v. 1.7.0 的对角函数中的内存泄漏。已在 v 1.7.1 中修复。

标签: python memory-leaks numpy


【解决方案1】:

问题是由numpy 1.7.0版本的对角线()函数引起的。 升级到 1.7.1 解决了问题!

解决方案由 numpy 邮件列表的 Sebastian Berg 和 Charles Harris 提供。

【讨论】:

  • 将有助于链接到实际修复的文档。
【解决方案2】:

如果需要,Python 会从操作系统分配内存。

如果它不再需要它,它可能会或可能不会再次返回它。

但如果它不返回它,内存将在后续分配中重复使用。你应该检查一下;但据说内存消耗不会增加更多。

关于您对内存消耗的估计:正如 azorius 已经写的那样,您的 temp 数组消耗 16 MB,而您的 A 数组消耗大约 200 * 8 = 1600 字节(+ 40 出于内部原因)。如果取 100 个字节,则为 164000 字节(加上列表中的一些字节)。

除此之外,我对你的内存消耗没有任何解释。

【讨论】:

  • 很清楚,谢谢。尽管如此,如果我将调用a.t() 的次数增加到200 次,我的进程就会被内核杀死。为什么会这样?我应该有 2*9 = 18 KiB 的内存使用量,不是吗?
  • @Pie86 查看我的帖子,200 个这种大小的 numpy 矩阵至少需要 3 GB 空间,您不要删除返回的矩阵,只删除创建它们的小类。
  • @azorius 没错,但我只保留返回的小矩阵。正如您所建议的,问题是没有与类一起删除的大临时矩阵temp
【解决方案3】:

我认为 sys.getsizeof 不会返回您期望的结果

你的 numpy 向量 A 是 64 位(8 字节) - 所以它占用(至少)

8 * 200 * 100 * 100 * 100 / (2.0**30) = 1.5625 GB

所以你至少应该在 100 个数组上使用 1.5 GB,最后几百毫克是用于索引大型 numpy 数据和 100 个对象的所有整数

无论numpy数组有多大,sys.getsizeof似乎总是返回80:

sys.getsizeof(np.zeros([200,1000,100])) # return 80
sys.getsizeof(np.zeros([20,100,10])) # return 80

在您的代码中,您删除了一个很小的工厂对象,它的 t 方法返回巨大的 numpy 数组,您将这些巨大的数组存储在一个名为 c 的列表中。 尝试删除 c,然后您应该重新获得大部分 RAM

【讨论】:

  • 8 * 200 * 100 * 100 = 16000000 B = 15625 kiB = 15.2587890625 MiB,比您预期的小三个数量级。但这仅适用于一个 A,我们有 100 个。较旧的系统可能会遇到这些尺寸的问题...
  • @glglgl 是的,但是 15 MB * 100 = 1.5GB... 我对我的帖子有点兴奋,因为我总是发布一个快速帖子然后编辑所有错误,谢谢你的指点这出来了:)
  • @glglgl 他认为他有内存泄漏,因为他正在丢失 1.6 GB,但即使他删除了 a,他的类也会返回 100 个 numpy 矩阵(他没有删除)所以它们仍在内存中
  • 好的,但是 temp 应该被删除(解除分配)。我不想像名字所暗示的那样存储它。而且 A 小得多,它只是 200 numpy.float64 乘以 100 次迭代! 8 * 200 * 100 = 160000 是我要使用的尺寸。
  • 你和 glglgl 是绝对正确的,我的错 :)... 如果我应该尝试猜测发生了什么是以下情况:python 在彼此内部有 3 层桶,它在其中分配内存,并且如果外部存储桶完全为空,它只能向操作系统“返还一大块内存”......但除了我没有想法之外,黑客解决方案是在 中生成临时init 然后重新配置它,这样如果 python 无法成功释放它们,你就不会生成 100 个它们
猜你喜欢
  • 2011-02-20
  • 2013-08-12
  • 2013-01-08
  • 2013-11-12
  • 2015-05-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多