Python垃圾收集:内存不再需要不发布到操作系统?

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

我已经用烧瓶编写了一个应用程序,并将芹菜用于一个长期运行的任务。当负载测试时,我注意到芹菜任务即使在完成任务之后也不会释放内存。所以我在谷歌上找到了这个小组讨论……

https://groups.google.com/forum/!主题/芹菜用户/jvc3ki3ktlw

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

另一篇文章在https://hbfs.wordpress.com/2013/01/08/python-memory-management-part-ii/上说

"但是从操作系统的角度来看,程序的大小是分配给python的总(最大)内存。由于python只在Windows上将内存返回到堆上的操作系统(分配除小对象以外的其他对象),因此如果在Linux上运行,则只能看到程序使用的总内存增加。"

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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"函数返回后,4MB内存也没有释放。当循环运行时,我检查了htop输出,常驻内存保持在14MB。因此,除非进程停止,否则内存将一直保留。

因此,如果芹菜工人在完成任务后没有被杀死,它将为自己保留记忆。我知道我可以使用max-tasks-per-child-config值来终止进程并生成一个新的进程。有没有其他方法可以将内存从Python进程返回到操作系统?.


我觉得你的测量方法和解释有点离谱。您使用的是resource.getrusageru_maxrss,这是进程的"高水印"。有关这意味着什么的详细信息,请参阅本讨论。简而言之,它是进程的峰值RAM使用率,但不一定是当前的。过程的某些部分可以换出等。

这也可能意味着进程释放了4mib,但操作系统没有回收内存,因为如果进程已经映射了内存,那么分配新的4mib会更快。为了使它更加复杂,程序可以并且确实使用"空闲列表",即不在活动使用中但未释放的内存块的列表。这也是使未来分配更快的常见技巧。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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()

结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
----------
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.