在上篇博客中,提到了对一个脚本进行的多次优化。当时以为已经优化得差不多了,但是当测试人员测试时,我才发现,踩到了Python的一个大坑。

在上文的优化中,对每500个用户,会进行一些计算并记录结果在磁盘文件中。原本以为这么做,这些结果就在磁盘文件中了,而不会再继续占用内存;但实际上,Python的大坑就是Python不会自动清理这些内存。这是由其本身实现决定的。具体原因网上多有文章介绍,这里就不copy了。

本篇博客将贴一个笔者的实验脚本,用以说明Python确实存在这么一个不释放内存的现象,然后提出一个解决方案,即: 先del,再显式调用gc.collect().

脚本和具体效果见下。

实验环境一:Win 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:
        else:  # way 2, 3
            del i
    print time()
    if way == 1 or way == 2:
    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并不会真正release内存,而是将其继续放在其内存池中;只有在显式调用gc.collect()时,才会真正release内存。


其实回到上一篇博客的脚本中,也让其引入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中可以明显看出。

我有一个python脚本,它可以抓取一些url。我有一个url列表,对于每个url,我得到html并用它做一些逻辑。在我使用Python2.7.6和LinuxMint17肉桂64位。在问题我的主要抓取对象(我为每个url实例)从未从内存释放,尽管没有对它的引用。有了这个问题,我的记忆就一直在快速增长(因为我的对象有时非常大,高达50MB)。在简化代码如下所示:def scrape_url(url... python话说会自己管理内存,实际上,对于占用很大内存的对象,并不会马上释放。举例,a=range(10000*10000),会发现内存飙升一个多G,del a 或者a=[]都不能将内存降下来。。 del 可以删除多个变量,del a,b,c,d import gc (garbage collector) del a gc.collect() 马上内存释放了。 Python的垃圾回收机制是内存管理的重要组成部分,它通过引用计数、标记-清除和分代回收等多种策略相结合的方式,有效地管理着程序中的内存资源。本文详细介绍了Python垃圾回收机制的基本概念、主要策略、应用场景以及触发与配置方式,并分析了其局限性和优化建议。通过深入理解和应用Python的垃圾回收机制,开发者可以编写出更高效、更稳定的程序。在未来的Python版本中,随着内存管理技术的不断进步和发展,我们可以期待垃圾回收机制在性能、效率和灵活性等方面得到进一步的提升和完善。 Python有自动垃圾回收机制,可以定期回收不再使用的对象所占用的内存。但是,如果对象之间存在循环引用,则需要手动断开循环来让垃圾回收机制回收内存。这里总结几种常用的Python内存释放方法。 numpy库提供了高效的数组操作,可以减少内存占用。我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。 在 Python 中使用 gc.collect() 方法清除内存使用 del 语句清除 Python 中的​​​​​​ ​内存方法用于清除或释放 Python 中未引用的内存。未引用的内存是无法访问且无法使用的内存。可选参数generation是一个整数,值的范围是0到2。它使用方法指定要收集的对象的生成。在 Python 中,寿命短的对象存储在0代中,而寿命较长的对象存储在1或2代中。每当调用具有默认generation值等于2的时,将清除垃圾收集器维护的列表。 内存溢出指的是内存越界,一种常见情况是调用栈溢出,栈内存不足的一种。 内存泄漏 内存申请后,用完没有释放,可用内存越来越少。 内存泄漏是最难发现的错误之一,除非用完内存或者调用malloc失败,否则不会导致任何问题。 windows平台下的内存泄漏检测: Visual Studio 调试器与C运行时库提供了一种检测内存泄漏的有效方法, 原理大致如下: 内存分配需要通过C运行时库实现,在分配内存释放内存时做好记录,然后在程序结束时对比分配内存释放内存的记录既可以确定是否有内存泄漏问 我试图将一个大型json对象加载到内存中,然后对数据执行一些操作。但是,我注意到在读取json文件之后,RAM有了很大的增加-即使对象超出了范围。这是密码import jsonimport objgraphimport gcfrom memory_profiler import profile@profiledef open_stuff():with open("bigjson.json", 'r...