Python 为什么numpy会保留这么多未使用的内存?

Python 为什么numpy会保留这么多未使用的内存?,python,numpy,memory,Python,Numpy,Memory,在上面的代码片段中,我分配了大约600MB的中等大小的numpy数组(当数组明显更小或更大时,这种行为不会发生,如果只使用Python对象,也不会发生),但是,当我解除分配阵列时,它仍然保留超过三分之二的内存,任何强制垃圾收集或删除都不会将该内存返回操作系统 我相当肯定这不是numpy中的内存泄漏,因为新的numpy分配将重用该内存(尽管Python分配不会),所以有人能解释为什么会发生这种情况吗 编辑:看起来这至少部分与系统分配器有关: $ python3 Python 3.5.2 (def

在上面的代码片段中,我分配了大约600MB的中等大小的numpy数组(当数组明显更小或更大时,这种行为不会发生,如果只使用Python对象,也不会发生),但是,当我解除分配阵列时,它仍然保留超过三分之二的内存,任何强制垃圾收集或删除都不会将该内存返回操作系统

我相当肯定这不是numpy中的内存泄漏,因为新的numpy分配将重用该内存(尽管Python分配不会),所以有人能解释为什么会发生这种情况吗


编辑:看起来这至少部分与系统分配器有关:

$ python3
Python 3.5.2 (default, Nov 12 2018, 13:43:14) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def mem():
...     with open("/proc/{}/status".format(os.getpid())) as f:
...             for line in f:
...                     if 'VmRSS' in line:
...                             return line.strip()
... 
>>> import gc
>>> import numpy
>>> import os
>>> numpy.version.version
'1.16.4'
>>> print(mem())
VmRSS:     27000 kB
>>> a = [numpy.random.random(size=(128, 128)) for _ in range(5000)]
>>> print(mem())
VmRSS:    668876 kB
>>> gc.collect()
0
>>> print(mem())
VmRSS:    668876 kB
>>> a = None
>>> print(mem())
VmRSS:    455432 kB
>>> gc.collect()
0
>>> print(mem())
VmRSS:    455432 kB
>>> del a
>>> print(mem())
VmRSS:    455432 kB
>>> gc.collect()
0
>>> print(mem())
VmRSS:    455432 kB

我现在把剩下的部分归结为无聊的旧内存碎片,但我想知道这里是否还有其他问题(numpy似乎确实做了一些事情)。

numpy是一个管理自己内存的C扩展,因此不涉及Python的垃圾收集器。Numpy将使用malloc()或calloc()在堆上为其数组分配空间,并在完成时使用free()释放该空间(当设置
a=None
时会发生这种情况)。堆分配器不一定会在内存空闲时将其释放回操作系统(),但是,这是否发生取决于如何从操作系统获得内存以及堆碎片等。但是,正如您所观察到的,该内存仍然可以被进程重用。

我希望得到更深入的回答,但是谢谢你让我一路走到那里。我猜这可能是因为当你进行大量中等大小的分配而不是使用更复杂的分配策略时,会出现固有的内存碎片?
$ LD_PRELOAD=/tmp/tmp.Rl0Ofo69sZ/jemalloc-5.2.1/lib/libjemalloc.so python3
Python 3.5.2 (default, Nov 12 2018, 13:43:14) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def mem():
...     with open("/proc/{}/status".format(os.getpid())) as f:
...             for line in f:
...                     if 'VmRSS' in line:
...                             return line.strip()
... 
>>> import numpy
>>> import os
>>> print(mem())
VmRSS:     33040 kB
>>> a = [numpy.random.random(size=(128, 128)) for _ in range(5000)]
>>> print(mem())
VmRSS:    694912 kB
>>> a = None
>>> print(mem())
VmRSS:    159568 kB