【问题标题】:Python Memory Leak Using binascii, zlib, struct, and numpy使用 binascii、zlib、struct 和 numpy 的 Python 内存泄漏
【发布时间】:2014-12-01 20:45:11
【问题描述】:

我有一个 python 脚本,它正在处理来自压缩 ASCII 的大量数据。一段时间后,它会耗尽内存。我不是在构建大型列表或字典。下面的代码说明了这个问题:

import struct
import zlib
import binascii
import numpy as np
import psutil
import os
import gc

process = psutil.Process(os.getpid())
n = 1000000
compressed_data = binascii.b2a_base64(bytearray(zlib.compress(struct.pack('%dB' % n, *np.random.random(n))))).rstrip()

print 'Memory before entering the loop is %d MB' % (process.get_memory_info()[0] / float(2 ** 20))
for i in xrange(2):
    print 'Memory before iteration %d is %d MB' % (i, process.get_memory_info()[0] / float(2 ** 20))
    byte_array = zlib.decompress(binascii.a2b_base64(compressed_data))
    a = np.array(struct.unpack('%dB' % (len(byte_array)), byte_array))
    gc.collect()
gc.collect()
print 'Memory after last iteration is %d MB' % (process.get_memory_info()[0] / float(2 ** 20))

打印出来:

Memory before entering the loop is 45 MB
Memory before iteration 0 is 45 MB
Memory before iteration 1 is 51 MB
Memory after last iteration is 51 MB

在第一次和第二次迭代之间,创建了 6 MB 的内存。如果我运行循环两次以上,内存使用量将保持在 51 MB。如果我把解压的代码放到它自己的函数中,并把实际的压缩数据提供给它,那么内存使用量将继续增长。我正在使用 Python 2.7。为什么内存在增加,如何纠正?谢谢。

【问题讨论】:

  • 我不会说,那是内存泄漏,这是正常的内存消耗。
  • 除了看起来很正常,正如@Daniel 所说,byte_arraya = np.array 怎么样?您的第一次迭代实例化它们之前输出内存使用情况。这听起来像是很多数据,很可能不会被垃圾收集器销毁,因为您在 for 循环范围内调用它。取消缩进(向左移动)gc.collect(),使其在for 循环之外运行,看看会发生什么。
  • @BorrajaX 在最后一次打印之前和循环退出之后添加了另一个 gc.collect,没有变化。对于所有打印语句,byte_array 和“a”变量不应该存在于内存中
  • 对不起,对不起......即使在for 循环之后,byte_arraya 也在你的范围内(我的错,它们不会被破坏)。在循环结束后(以及在您刚刚添加的第二个 gc.collect() 之前)执行 byte_array = None a=None... 现在我自己很好奇 :-)
  • @BorrajaX 在那些设置为 None 的语句中添加了它,它清除了内存,解决了我的担忧。我误解了 Python 范围,我更习惯于 Java。无论如何,我的代码中仍然存在问题,但上面的示例没有正确显示它。谢谢

标签: python memory-leaks


【解决方案1】:

通过 cmets,我们弄清楚了发生了什么:

主要问题是在for 循环中声明的变量在循环结束后不会被销毁。它们仍然可访问,指向它们在上次迭代中收到的值:

>>> for i in range(5):
...     a=i
...
>>> print a
4

这就是正在发生的事情:

  • 第一次迭代:print 显示为 45MB,这是在实例化 byte_arraya 之前的内存
  • 代码实例化了这两个冗长的变量,使内存达到 51MB
  • 第二次迭代:循环第一次运行时实例化的两个变量仍然存在。
  • 在第二次迭代的中间,byte_arraya 被新的实例化覆盖。最初的变量被销毁,但被同样长的变量替换。
  • for 循环结束,但 byte_arraya 仍可在代码中访问,因此不会被第二次 gc.collect() 调用破坏。

将代码更改为:

for i in xrange(2):
   [ . . . ]
byte_array = None
a = None
gc.collect()

使byte_arraya 保留的内存无法访问,因此被释放。

在这个 SO 答案中有更多关于 Python 垃圾收集的信息:https://stackoverflow.com/a/4484312/289011

另外,How do I determine the size of an object in Python? 可能值得一看。不过,这很棘手……如果您的对象是指向其他对象的列表,那么 size 是多少?列表中指针的总和?这些指针指向的对象的大小总和?

【讨论】:

    猜你喜欢
    • 2015-01-30
    • 1970-01-01
    • 2013-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 2021-12-09
    相关资源
    最近更新 更多