Python内存泄漏


180

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

在不详细介绍脚本的情况下,我有两个问题:

  1. 是否有可遵循的“最佳实践”,以防止泄漏发生?
  2. 有什么技术可以调试Python中的内存泄漏?

5
我发现这个食谱很有帮助。
David Schein

似乎打印出了太多有用的数据
Casebash 2009年

1
@Casebash:如果该函数可以打印任何内容,则说明您确实在做错事情。它列出了对象,该对象的__del__方法除其循环外不再被引用。由于存在问题,因此无法中断该周期__del__。修理它!
Helmut Grohne,2010年

Answers:



83

我尝试了前面提到的大多数选项,但是发现这个小而直观的软件包是最好的: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的文档,特别是确定内存泄漏一节。


5
值得注意的是,它pympler可能很。如果您正在做半实时的操作,它可能会完全削弱您的应用程序性能。
假名称

@sebpiq很奇怪,我也遇到了同样的事情……你知道为什么会这样吗?快速浏览源代码并没有真正的见识。
linusg '18

25

让我推荐我创建的mem_top工具

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

它只是立即显示Python程序中内存泄漏的主要嫌疑人


1
这是真的......但它的使用/结果解释的方式很少给
me_

@me_,此工具同时记录了“用法”和“解释结果”两部分。我是否应该添加诸如“ refs是来自对象的引用计数,类型是该类型的对象计数,字节是对象的大小”之类的解释-将其记录下来不是很明显吗?
Denis Ryzhkov

该工具的用法文档仅在一行中写着“时不时:logging.debug(mem_top())”,而对结果的解释是作者在没有上下文的情况下的真实错误跟踪经验……这并不是一个技术指标一个开发人员正是他们在看什么...我没有敲定你的答案...它显示了高水平的可疑嫌疑人的账单...它没有提供足够的文档来完全理解使用结果...例如,在“解释结果”输出中,为什么“ GearmanJobRequest”显然是一个问题?没有解释为什么...
me_

1
我想我是无意中敲了敲你的工具,你是作者……这不是故意的……
me_

6
@me_,我刚刚将下一步添加到“用法”中,添加了“计数器”部分,并添加了解释说明为何Gearman确实是该实际示例中的可疑对象,并在代码中记录了“ mem_top()”的每个可选参数,并将其全部上传为v0.1.7-请查看是否还有其他可以改进的地方。谢谢!)
Denis Ryzhkov

18

从Python 3.4开始,Tracemalloc模块已集成为内置模块,并且显然,它也可以作为第三方库用于Python的早期版本(尽管尚未测试)。

该模块能够输出分配最多内存的精确文件和行。恕我直言,此信息比每种类型分配的实例数更有价值(99%的时间最终是很多元组,这是一个线索,但在大多数情况下几乎没有帮助)。

我建议您将tracemalloc与pyrasite结合使用。每10个中有9 pyrasite外壳中运行前10个代码段,将为您提供足够的信息和提示,以在10分钟内修复泄漏。但是,如果仍然无法找到泄漏的原因,pyrasite-shell与该线程中提到的其他工具的组合可能也会为您提供更多提示。您还应该查看吡铁矿提供的所有其他帮助程序(例如内存查看器)。



12

您应该特别查看全局数据或静态数据(寿命长的数据)。

当这些数据不受限制地增长时,您也会在Python中遇到麻烦。

垃圾收集器只能收集不再引用的数据。但是您的静态数据可以连接应释放的数据元素。

另一个问题可能是内存循环,但是至少从理论上讲,垃圾收集器应该找到并消除循环-至少只要不将其挂接到某些长期存在的数据上即可。

什么样的长期数据特别麻烦?在所有列表和字典上都有一个很好的外观-它们可以不受限制地增长。在字典中,您甚至可能看不到麻烦,因为当您访问字典时,字典中的键数可能对您来说不是很明显。



4

就最佳实践而言,请留意递归函数。就我而言,我遇到了递归问题(不需要递归)。我正在做什么的简化示例:

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()

7
如果您达到递归深度限制,以这种方式使用递归也会中断,因为Python不会优化尾调用。默认情况下,这是1000个递归调用。
Lie Ryan

3

不确定python中内存泄漏的“最佳实践”,但是python应该通过垃圾回收器清除它自己的内存。因此,主要是我从检查一些简短的循环列表开始,因为它们不会被垃圾收集器接收。


3
或正在永远保持等对对象的引用
无光泽b

3
你们能否提供永久保存的循环列表和对象的示例?
丹尼尔(Daniel)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.