在尝试微调某些 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
代码中的语句实际上最终将相应对象的引用计数减少到零(从而使它们有资格进行垃圾收集)。通常,当对象经历垃圾回收时,我希望在增量列中看到负条目。谁能解释一下这里发生的事情吗?
注意:此测试在 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) 上运行。