16

我正在尝试调试我的大型 Python 应用程序的内存问题。大部分内存都在numpy由 Python 类管理的数组中,因此Heapy等是无用的,因为它们不考虑numpy数组中的内存。所以我尝试使用 MacOSX (10.7.5) 活动监视器(或者top如果你愿意的话)手动跟踪内存使用情况。我注意到以下奇怪的行为。在普通的python解释器外壳(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

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

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

这似乎确实增加了,因为在我的实际应用程序中,我最终得到了超过 2 GB 的内存,我永远无法从 Python 解释器中取回。这是 Python 根据使用历史保留越来越多内存的预期行为吗?如果是,那么对于我的情况,Activity Monitor 和 Heapy 一样没用。有什么东西不是没用的吗?

4

1 回答 1

18

从Numpy 的释放内存策略中读取,似乎numpy没有内存分配/释放进行任何特殊处理。它只是free()在引用计数变为零时调用。事实上,用任何内置的 python 对象来复制这个问题是很容易的。问题在于操作系统级别。

纳撒尼尔史密斯在链接线程中的一个回复中解释了正在发生的事情:

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

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

因此,似乎没有通用的解决方案。分配许多小对象会导致工具描述的“高内存使用率”,即使您会在需要时重用它,而分配大对象不会显示大内存释放后的使用,因为内存被操作系统回收。

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

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

In [2]: del a

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

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

In [2]: del a

内存回收。

为了避免内存问题,您应该分配大数组并使用它们(也许使用视图来“模拟”小数组?),或者尽量避免同时拥有许多小数组。如果您有一些创建小对象的循环,您可能会在每次迭代时显式释放不需要的对象,而不是仅在最后执行此操作。


我相信Python 内存管理对如何在 python 中管理内存提供了很好的见解。请注意,在“操作系统问题”之上,python 添加了另一层来管理内存区域,这可能会导致小对象的高内存使用率。

于 2013-08-19T10:44:44.147 回答