如何从Python中将内存释放回操作系统?
我有一段代码,看起来像这样:
def memoryIntensiveFunction(x):
largeTempVariable = Intermediate(x)
processFunction(largeTempVariable,x)
问题是,变量 temp
在我的一个测试案例中大约占用了 500 MB 的内存,但当 memoryIntensiveFunction
执行完后,这部分内存并没有被操作系统回收。我知道这一点是因为使用 guppy
工具进行内存分析时显示 largeTempVariable
已经被释放(也就是说,在 Python 内部),但 psutil
显示它并没有被释放。我猜我看到的现象就是这里描述的情况。问题是这个过程运行时间很长(也就是几个小时),而 memoryIntensiveFunction
只在开始时运行一次,之后再也不运行,所以我不想在几个小时内一直占用这 500 MB 的内存。
我找到的一个解决方案在这里和在这里建议使用一个单独的进程。虽然多进程会有自己的开销,但在我的情况下是值得的。不过,这样的话就需要重新设计调用 memoryIntensiveFunction
的代码,让它返回 x
作为结果,而不是直接修改它。更麻烦的是,我的对象 x
不能被序列化(它大量使用了 boost python 扩展)。要让 x
可以被序列化,工作量会很大。
还有没有我没有考虑到的其他选项呢?
1 个回答
2
这件事挺有意思的,我试着重现你的问题,发现简单的“del”就足够了。
为了演示,你可以运行以下代码:
import itertools
import pdb
def test():
a = "a"
for _ in itertools.repeat(None, 30):
a += a
pdb.set_trace()
del a
pdb.set_trace()
test()
在第一个断点时,你会看到大约使用了1GB的内存(你要找的是python3.3的入口):
Private + Shared = RAM used Program
4.0 KiB + 9.0 KiB = 13.0 KiB VisualGDB-DisownTTY-r1
4.0 KiB + 15.0 KiB = 19.0 KiB sharing-tests
4.0 KiB + 19.5 KiB = 23.5 KiB dhcpcd
4.0 KiB + 31.5 KiB = 35.5 KiB gdb
4.0 KiB + 36.0 KiB = 40.0 KiB vim [deleted]
4.0 KiB + 38.0 KiB = 42.0 KiB systemd-udevd
40.0 KiB + 10.0 KiB = 50.0 KiB init
24.0 KiB + 135.0 KiB = 159.0 KiB agetty (6)
12.0 KiB + 150.0 KiB = 162.0 KiB su (3)
88.0 KiB + 103.0 KiB = 191.0 KiB syslog-ng (2)
152.0 KiB + 55.0 KiB = 207.0 KiB crond
172.0 KiB + 81.0 KiB = 253.0 KiB python3.4
580.0 KiB + 220.5 KiB = 800.5 KiB sshd (3)
768.0 KiB + 932.0 KiB = 1.7 MiB bash (13)
2.8 MiB + 118.0 KiB = 2.9 MiB mongod
7.4 MiB + 109.0 KiB = 7.5 MiB tmux [deleted] (2)
1.0 GiB + 1.2 MiB = 1.0 GiB python3.3
---------------------------------
1.0 GiB
=================================
然后在第二个断点,当我们删除变量后,内存就释放了:
Private + Shared = RAM used Program
4.0 KiB + 9.0 KiB = 13.0 KiB VisualGDB-DisownTTY-r1
4.0 KiB + 15.0 KiB = 19.0 KiB sharing-tests
4.0 KiB + 19.5 KiB = 23.5 KiB dhcpcd
4.0 KiB + 31.5 KiB = 35.5 KiB gdb
4.0 KiB + 36.0 KiB = 40.0 KiB vim [deleted]
4.0 KiB + 38.0 KiB = 42.0 KiB systemd-udevd
40.0 KiB + 10.0 KiB = 50.0 KiB init
24.0 KiB + 135.0 KiB = 159.0 KiB agetty (6)
12.0 KiB + 150.0 KiB = 162.0 KiB su (3)
88.0 KiB + 103.0 KiB = 191.0 KiB syslog-ng (2)
152.0 KiB + 55.0 KiB = 207.0 KiB crond
172.0 KiB + 81.0 KiB = 253.0 KiB python3.4
584.0 KiB + 220.5 KiB = 804.5 KiB sshd (3)
768.0 KiB + 928.0 KiB = 1.7 MiB bash (13)
2.8 MiB + 118.0 KiB = 2.9 MiB mongod
5.1 MiB + 1.2 MiB = 6.3 MiB python3.3
7.4 MiB + 109.0 KiB = 7.5 MiB tmux [deleted] (2)
---------------------------------
20.3 MiB
=================================
现在如果我们把函数中的“del”去掉,并在test()之后设置一个断点:
import itertools
import pdb
def test():
a = "a"
for _ in itertools.repeat(None, 30):
a += a
pdb.set_trace()
test()
pdb.set_trace()
那么在我们结束之前,内存确实不会被释放:
Private + Shared = RAM used Program
4.0 KiB + 9.0 KiB = 13.0 KiB VisualGDB-DisownTTY-r1
4.0 KiB + 15.0 KiB = 19.0 KiB sharing-tests
4.0 KiB + 19.5 KiB = 23.5 KiB dhcpcd
4.0 KiB + 31.5 KiB = 35.5 KiB gdb
4.0 KiB + 36.0 KiB = 40.0 KiB vim [deleted]
4.0 KiB + 38.0 KiB = 42.0 KiB systemd-udevd
40.0 KiB + 10.0 KiB = 50.0 KiB init
24.0 KiB + 135.0 KiB = 159.0 KiB agetty (6)
12.0 KiB + 150.0 KiB = 162.0 KiB su (3)
160.0 KiB + 53.0 KiB = 213.0 KiB crond
172.0 KiB + 81.0 KiB = 253.0 KiB python3.4
628.0 KiB + 219.5 KiB = 847.5 KiB sshd (3)
836.0 KiB + 152.0 KiB = 988.0 KiB syslog-ng (2)
752.0 KiB + 957.0 KiB = 1.7 MiB bash (13)
2.8 MiB + 113.0 KiB = 2.9 MiB mongod
7.4 MiB + 108.0 KiB = 7.6 MiB tmux [deleted] (2)
1.0 GiB + 1.1 MiB = 1.0 GiB python3.3
---------------------------------
1.0 GiB
=================================
所以我的建议是?用完之后就把它删掉,不再需要的时候就行了;)