对于小型/大型numpy数组,释放方式是否有所不同?

发布于 2021-01-29 19:01:13

我正在尝试使用大型Python应用程序调试内存问题。大部分内存在numpyPython类管理的数组中,因此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一样没用。那里有没有没有用的东西吗?

关注者
0
被浏览
140
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

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

    纳撒尼尔·史密斯(Nathaniel Smith)在链接线程的其中一封答复中写了一个解释:

    通常,进程可以从OS请求内存,但 不能将其退还给
    内存。在C级别上,如果您调用free(),那么实际发生的情况是您进程中的内存管理库自己记录了该内存未使用的情况,并且可能会在将来malloc()从操作系统中将其返回,但是从操作系统的角度来看仍然是“已分配”。(并且python在malloc()/之上使用了另一个类似的系统
    free(),但这并没有真正改变任何东西。)因此,您看到的OS内存使用率通常是“高水位线”,即您的进程曾经需要的最大内存量。

    唯一的例外是,对于较大的单个分配(例如,如果您创建一个多兆字节的数组),则使用不同的机制。如此大的内存分配 可以
    释放回OS。因此,具体来说,可能是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还添加了另一层来管理内存区域,这可能会导致小对象占用大量内存。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看