对于小型/大型numpy阵列,释放是否有不同的处理方式?

Ste*_*fan 15 python memory memory-leaks memory-management numpy

我试图用我的大型Python应用程序调试内存问题.大多数内存都在numpy由Python类管理的数组中,所以Heapy等没用,因为它们不考虑numpy数组中的内存.所以我尝试使用MacOSX(10.7.5)活动监视器(或者top如果你愿意)手动跟踪内存使用情况.我注意到以下奇怪的行为.在普通的python解释器shell(2.7.3)上:

import numpy as np # 1.7.1
# Activity Monitor: 12.8 MB
a = np.zeros((1000, 1000, 17)) # a "large" array
# 142.5 MB
del a
# 12.8 MB (so far so good, the array got freed)
a = np.zeros((1000, 1000, 16)) # a "small" array
# 134.9 MB
del a
# 134.9 MB (the system didn't get back the memory)
import gc
gc.collect()
# 134.9 MB
Run Code Online (Sandbox Code Playgroud)

无论我做什么,Python会话的内存占用量将永远不会再低于134.9 MB.所以我的问题是:

为什么数组的资源大于1000x1000x17x8字节(在我的系统上经验找到)正确地返回给系统,而较小数组的内存似乎永远被Python解释器所困?

这看起来确实很棘手,因为在我的实际应用程序中,我最终得到了超过2 GB的内存,我永远无法从Python解释器中获取.Python的预期行为是否会根据使用历史记录保留越来越多的内存?如果是的话,那么Activity Monitor就像Heapy一样无用.那里有什么不是没用的吗?

Bak*_*riu 18

从读NumPy的政策释放的内存好像numpy没有有内存分配/释放的任何特殊处理.它只是free()在引用计数变为零时调用.事实上,使用任何内置的python对象复制问题都非常容易.问题在于操作系统级别.

纳撒尼尔·史密斯已经在链接线程的一个回复中写了一个解释:

通常,进程可以从操作系统请求内存,但是它们无法返回.在C级别,如果你调用free(),那么实际发生的是你的进程中的内存管理库为自己做了一个注释,说明没有使用该内存,并且可能从未来返回它malloc(),但是从操作系统的角度来看它仍然"分配".(并且python在malloc()/ 上使用另一个类似的系统 free(),但这并没有真正改变任何东西.)因此,您看到的操作系统内存使用量通常是"高水位线",即您的进程所需的最大内存量.

例外情况是,对于大型单个分配(例如,如果创建一个多兆字节数组),则使用不同的机制.这样大的内存分配可以释放回操作系统.所以它可能特别是numpy你的程序的非部分产生你看到的问题.

因此,似乎没有解决问题的一般解决方案.分配许多小对象将导致工具分析的"高内存使用",即使它在需要时会被重用,而分配大对象也不会显示大内存释放后的使用,因为操作系统回收了内存.

您可以验证这个分配内置python对象:

In [1]: a = [[0] * 100 for _ in range(1000000)]

In [2]: del a
Run Code Online (Sandbox Code Playgroud)

在这段代码之后我可以看到内存没有被回收,同时:

In [1]: a = [[0] * 10000 for _ in range(10000)]

In [2]: del a
Run Code Online (Sandbox Code Playgroud)

记忆回收了.

为了避免出现内存问题,你应该分配的大阵列,并与他们合作(也许使用视图来"模拟"小数组?),还是尽量避免有许多小数组在同一时间.如果您有一些创建小对象的循环,您可能会在每次迭代时显式释放不需要的对象,而不是仅在最后执行此操作.


我相信Python内存管理可以很好地了解如何在python中管理内存.请注意,除了"操作系统问题"之外,python还添加了另一个层来管理内存竞技场,这可能会导致小对象的高内存使用率.