Python 垃圾收集:不再需要的内存未释放到 OS?

Python Garbage Collection: Memory no longer needed not released to OS?

我用 flask 编写了一个应用程序,并使用 celery 完成了一项较长的 运行 任务。在负载测试时,我注意到 celery 任务即使在完成任务后也不会释放内存。所以我用谷歌搜索并找到了这个小组讨论..

https://groups.google.com/forum/#!topic/celery-users/jVc3I3kPtlw

在那次讨论中它说,这就是 python 的工作原理。

https://hbfs.wordpress.com/2013/01/08/python-memory-management-part-ii/ 上的文章也说

"But from the OS’s perspective, your program’s size is the total (maximum) memory allocated to Python. Since Python returns memory to the OS on the heap (that allocates other objects than small objects) only on Windows, if you run on Linux, you can only see the total memory used by your program increase."

我用 Linux。所以我写了下面的脚本来验证它。

import gc
def memory_usage_psutil():
    # return the memory usage in MB
    import resource
    print 'Memory usage: %s (MB)' % (resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1000.0)

def fileopen(fname):
    memory_usage_psutil()# 10 MB
    f = open(fname)
    memory_usage_psutil()# 10 MB
    content = f.read()
    memory_usage_psutil()# 14 MB

def fun(fname):
    memory_usage_psutil() # 10 MB
    fileopen(fname)
    gc.collect()
    memory_usage_psutil() # 14 MB

import sys
from time import sleep
if __name__ == '__main__':
    fun(sys.argv[1])
    for _ in range(60):
        gc.collect()
        memory_usage_psutil()#14 MB ...
        sleep(1)

输入的是一个 4MB 的文件。即使在从 'fileopen' 函数中 returning 之后,4MB 内存也没有被释放。我在循环 运行 时检查了 htop 输出,驻留内存保持在 14MB。因此,除非进程停止,否则内存会保留。

因此,如果 celery worker 在其任务完成后未被杀死,它将为自己保留内存。我知道我可以使用 max_tasks_per_child 配置值来终止进程并生成一个新进程。 是否有任何其他方法可以将return内存从python进程OS转移到OS?

我觉得你的测量方法和解释有点不对。您正在使用 ru_maxrssresource.getrusage, which is the "high watermark" of the process. See this 讨论来了解这意味着什么的详细信息。简而言之,它是您进程的 RAM 使用峰值,但不一定是当前的。部分过程可以换出等

这也可能意味着进程已经释放了那 4MiB,但是 OS 没有回收内存,因为如果进程已经映射了内存,分配新的 4MiB 会更快。为了使它变得更复杂,程序可以并且确实使用 "free lists",未被激活但未被释放的内存块列表。这也是让未来分配更快的常用技巧。

我写了一个简短的脚本来演示虚拟内存使用和最大 RSS 之间的区别:

import numpy as np
import psutil
import resource


def print_mem():
    print("----------")
    print("ru_maxrss: {:.2f}MiB".format(
            resource.getrusage(resource.RUSAGE_SELF).ru_maxrss / 1024))
    print("virtual_memory.used: {:.2f}MiB".format(
            psutil.virtual_memory().used / 1024 ** 2))


print_mem()
print("allocating large array (80e6,)...")
a = np.random.random(int(80e6))

print_mem()
print("del a")
del a

print_mem()
print("read testdata.bin (~400MiB)")
with open('testdata.bin', 'rb') as f:
    data = f.read()

print_mem()
print("del data")
del data

print_mem()

结果是:

----------
ru_maxrss: 22.89MiB
virtual_memory.used: 8125.66MiB
allocating large array (80e6,)...
----------
ru_maxrss: 633.20MiB
virtual_memory.used: 8731.85MiB
del a
----------
ru_maxrss: 633.20MiB
virtual_memory.used: 8121.66MiB
read testdata.bin (~400MiB)
----------
ru_maxrss: 633.20MiB
virtual_memory.used: 8513.11MiB
del data
----------
ru_maxrss: 633.20MiB
virtual_memory.used: 8123.22MiB

清楚了ru_maxrss是怎么记住最大RSS的,但是现在的使用率到最后还是下降了

关于 psutil.virtual_memory().used 的注释:

used: memory used, calculated differently depending on the platform and designed for informational purposes only.