手动释放Python内存:del与gc.collect()的实践解析
本文通过实验揭示Python中del语句并不真正释放内存,而是归还给内存池。只有通过显式调用gc.collect()才能强制回收对象。文章对比了不同环境下的内存消耗峰值与实时状态,指出在多线程或特定场景下gc的触发时机可能受系统可用内存影响,建议结合del与gc.collect()以优化脚本内存表现。
在前一篇博客中,我们讨论了对某个脚本进行的多次优化。当时自认为优化已臻完善,然而在测试人员介入测试后,我才意识到自己掉进了Python的一个巨大陷阱。
此前的优化方案中,每处理500个用户便会执行计算并将结果写入磁盘文件。原本假设写入磁盘后,这些数据便不再占用内存;但Python的机制决定了它不会自动清理这部分内存。这一现象源于Python自身的实现原理,具体原因网上已有大量文章详述,此处不再赘述。
本篇博客将展示一个实验脚本,旨在证明Python确实存在不自动释放内存的现象,并提供一种解决方案:先使用del语句,再显式调用gc.collect()。脚本代码及具体效果如下所示。
实验环境一:Windows 7系统,Python 2.7版本。
from time import sleep, time
import gc
def mem(way=1):
print time()
for i in range(10000000):
if way == 1:
pass
else: # way 2, 3
del i
print time()
if way == 1 or way == 2:
pass
else: # way 3
gc.collect()
print time()
if __name__ == "__main__":
print "Test way 1: just pass"
mem(way=1)
sleep(20)
print "Test way 2: just del"
mem(way=2)
sleep(20)
print "Test way 3: del, and then gc.collect()"
mem(way=3)
sleep(20)
运行结果如下:
Test way 1: just pass 1426688589.47 1426688590.25 1426688590.25 Test way 2: just del 1426688610.25 1426688611.05 1426688611.05 Test way 3: del, and then gc.collect() 1426688631.05 1426688631.85 1426688631.95
对于Way 1和Way 2,结果完全一致。程序内存消耗峰值达到326772KB,在sleep 20秒时,实时内存消耗为244820KB。
对于Way 3,程序内存消耗峰值与前述相同,但在sleep期间,实时内存消耗降至仅6336KB。
实验环境二:Ubuntu 14.10系统,Python 2.7.3版本。
运行结果:
Test way 1: just pass 1426689577.46 1426689579.41 1426689579.41 Test way 2: just del 1426689599.43 1426689601.1 1426689601.1 Test way 3: del, and then gc.collect() 1426689621.12 1426689622.8 1426689623.11
ubuntu@my_machine:~$ ps -aux | grep test_mem Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html ubuntu 9122 10.0 6.0 270916 245564 pts/1 S+ 14:39 0:03 python test_mem.py ubuntu 9134 0.0 0.0 8104 924 pts/2 S+ 14:40 0:00 grep --color=auto test_mem ubuntu@my_machine:~$ ps -aux | grep test_mem Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html ubuntu 9122 10.0 6.0 270916 245564 pts/1 S+ 14:39 0:03 python test_mem.py ubuntu 9134 0.0 0.0 8104 924 pts/2 S+ 14:40 0:00 grep --color=auto test_mem ubuntu@my_machine:~$ ps -aux | grep test_mem Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html ubuntu 9122 11.6 0.1 30956 5608 pts/1 S+ 14:39 0:05 python test_mem.py
结论:
以上实验表明,当调用del时,Python实际上并未真正释放内存,而是将其保留在内存池中;只有在显式调用gc.collect()时,才会真正执行内存释放操作。
进一步分析:
回顾上一篇博客中的脚本,我们引入gc.collect()并编写监控脚本来观察内存消耗情况:
while ((1)); do ps -aux | sort -n -k5,6 | grep my_script; free; sleep 5; done
结果显示:内存并不会在每500个用户一组执行完毕后立即恢复,而是持续消耗,直到系统可用内存仅剩约70MB时,gc才似乎开始发挥作用。在本环境中,机器使用的是Cloud instance,总内存为2G,可用内存约为1G,该脚本的常规内存消耗在900M至1G之间。换言之,对于这个脚本而言,gc并未立即生效,而是在系统可用内存从1-1.2G下降至仅剩70M左右时,gc才开始起作用。这一点颇为奇怪,不确定是否与该脚本在Thread中使用gc.collect()有关,亦或是gc的作用机制本身不可控。笔者尚未对此进行相关实验,可能将在下篇博客中继续探讨。
但可以肯定的是,若不使用gc.collect(),原脚本将会耗尽系统内存并被系统杀死。这一点可以从syslog日志中明显看出。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









