numpy.ndarray对象不是垃圾收集

时间:2015-07-15 05:29:06

标签: python memory-management memory-leaks garbage-collection memory-profiling

在试图微调一些C / C ++函数的Python绑定中的一些内存泄漏时,我想到了一些与Numpy数组的垃圾收集有关的奇怪行为。

我创建了几个简化案例,以便更好地解释行为。代码使用memory_profiler运行,后面的输出紧随其后。看来,当涉及到NumPy数组时,Python的垃圾收集工作不正常:

# File deallocate_ndarray.py
@profile
def ndarray_deletion():
    import numpy as np
    from gc import collect
    buf = 'abcdefghijklmnopqrstuvwxyz' * 10000
    arr = np.frombuffer(buf)
    del arr
    del buf
    collect()
    y = [i**2 for i in xrange(10000)]
    del y
    collect()

if __name__=='__main__':
    ndarray_deletion()

使用以下命令,我调用了memory_profiler

python -m memory_profiler deallocate_ndarray.py

这就是我得到的:

Filename: deallocate_ndarray.py
Line #    Mem usage    Increment   Line Contents
================================================
 5   10.379 MiB    0.000 MiB   @profile
 6                             def ndarray_deletion():
 7   17.746 MiB    7.367 MiB       import numpy as np
 8   17.746 MiB    0.000 MiB       from gc import collect
 9   17.996 MiB    0.250 MiB       buf = 'abcdefghijklmnopqrstuvwxyz' * 10000
10   18.004 MiB    0.008 MiB       arr = np.frombuffer(buf)
11   18.004 MiB    0.000 MiB       del arr
12   18.004 MiB    0.000 MiB       del buf
13   18.004 MiB    0.000 MiB       collect()
14   18.359 MiB    0.355 MiB       y = [i**2 for i in xrange(10000)]
15   18.359 MiB    0.000 MiB       del y
16   18.359 MiB    0.000 MiB       collect()

我不明白为什么即使强制调用collect也不会通过释放一些内存来减少程序的内存使用量。而且,即使Numpy数组由于底层C构造而不能正常运行,为什么列表(纯Python)不会被垃圾收集?

我知道del没有直接调用基础__del__方法,但您会注意到代码中的所有del语句实际上最终减少了相应对象的引用计数为零(从而使它们有资格进行垃圾收集AFAIK)。通常,当对象进行垃圾回收时,我希望在增量列中看到负数条目。任何人都可以了解这里发生的事情吗?

注意:此测试在OS X 10.10.4,Python 2.7.10(conda),Numpy 1.9.2(conda),Memory Profiler 0.33(conda-binstar),psutil 2.2.1(conda)上运行。

1 个答案:

答案 0 :(得分:3)

为了看到收集的内存垃圾,我不得不将buf的大小增加几个数量级。可能memory_profiler的大小太小而无法检测到更改(它会查询操作系统,因此测量结果不是很精确),或者它太小而无法让Python垃圾收集器关注,我不知道。

例如,在因子buf中将10000替换为100000000会产生

Line #    Mem usage    Increment   Line Contents
================================================
21   10.289 MiB    0.000 MiB   @profile
22                             def ndarray_deletion():
23   17.309 MiB    7.020 MiB       import numpy as np
24   17.309 MiB    0.000 MiB       from gc import collect
25 2496.863 MiB 2479.555 MiB       buf = 'abcdefghijklmnopqrstuvwxyz' * 100000000
26 2496.867 MiB    0.004 MiB       arr = np.frombuffer(buf)
27 2496.867 MiB    0.000 MiB       del arr
28   17.312 MiB -2479.555 MiB       del buf
29   17.312 MiB    0.000 MiB       collect()
30   17.719 MiB    0.406 MiB       y = [i**2 for i in xrange(10000)]
31   17.719 MiB    0.000 MiB       del y
32   17.719 MiB    0.000 MiB       collect()