我有一个长时间运行的脚本,如果让它运行足够长的时间,它将占用系统上的所有内存。

在不讨论剧本细节的情况下,我有两个问题:

是否有任何“最佳实践”可以遵循,这将有助于防止泄漏的发生? 在Python中有哪些调试内存泄漏的技术?


当前回答

这绝不是详尽的建议。但是,在考虑避免未来的内存泄漏(循环)时,要记住的第一件事是确保任何接受回调引用的东西都应该将该回调存储为弱引用。

其他回答

让我推荐我创建的mem_top工具

它帮助我解决了一个类似的问题

它会立即显示Python程序中最可能出现内存泄漏的地方

至于最佳实践,请密切关注递归函数。在我的例子中,我遇到了递归的问题(在不需要的地方)。举个简单的例子:

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    if my_flag:  # restart the function if a certain flag is true
        my_function()

def main():
    my_function()

以这种递归方式操作不会触发垃圾收集并清除函数的剩余部分,因此每次通过内存使用都会越来越大。

我的解决方案是从my_function()中提取递归调用,并在再次调用时使用main()句柄。这样,函数自然结束,并自行清理。

def my_function():
    # lots of memory intensive operations
    # like operating on images or huge dictionaries and lists
    .....
    my_flag = True
    .....
    return my_flag

def main():
    result = my_function()
    if result:
        my_function()

这绝不是详尽的建议。但是,在考虑避免未来的内存泄漏(循环)时,要记住的第一件事是确保任何接受回调引用的东西都应该将该回调存储为弱引用。

我尝试了前面提到的大多数选项,但发现这个小而直观的包是最好的:pympler

跟踪没有被垃圾回收的对象非常简单,请看这个小例子:

通过PIP Install pympler安装包

from pympler.tracker import SummaryTracker
tracker = SummaryTracker()

# ... some code you want to investigate ...

tracker.print_diff()

输出显示已添加的所有对象,以及它们所消耗的内存。

样例输出:

                                 types |   # objects |   total size
====================================== | =========== | ============
                                  list |        1095 |    160.78 KB
                                   str |        1093 |     66.33 KB
                                   int |         120 |      2.81 KB
                                  dict |           3 |       840 B
      frame (codename: create_summary) |           1 |       560 B
          frame (codename: print_diff) |           1 |       480 B

这个包提供了更多的特性。检查pympler的文档,特别是识别内存泄漏一节。

要检测和定位长时间运行进程的内存泄漏,例如在生产环境中,您现在可以使用堆栈影响。它在下面使用tracemalloc。更多信息在这篇文章。