Python内存泄漏
我有一个长时间运行的脚本,如果运行时间足够长,将占用我系统上的所有内存。
没有详细说明剧本,我有两个问题:
- 是否有任何“最佳实践”要遵循,这将有助于防止泄漏发生?
- 有哪些技术来debuggingPython中的内存泄漏?
看看这篇文章: 跟踪python内存泄漏
另外请注意, 垃圾收集模块实际上可以设置debugging标志。 看看set_debug
函数。 另外,请查看Gnibbler的代码来确定调用后创build的对象的types。
我尝试了以前提到的大多数选项,但发现这个小而直观的包是最好的: 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
这个包提供了更多的function。 检查pympler的文档 ,特别是识别内存泄漏部分。
让我推荐mem_top工具,
这帮助我解决了类似的问题。
它只是立即显示在Python程序中的内存泄漏的顶级嫌犯。
你应该特别看看你的全球或静态数据(长寿命数据)。
当这个数据不受限制地增长时,你也可以在Python中遇到麻烦。
垃圾收集器只能收集数据,不再被引用。 但是,您的静态数据可以连接应该释放的数据元素。
另一个问题可能是内存周期,但至less在理论上垃圾收集器应该find并消除周期 – 至less只要不吸引长时间的活动数据。
什么样的长时间数据特别麻烦? 仔细阅读任何名单和字典 – 他们可以没有任何限制地增长。 在字典中,你甚至可能没有看到自从访问字典以来所遇到的麻烦,字典中的键的数量可能不会很大。
不知道python的内存泄漏的“最佳实践”,但python应清除它的垃圾收集器自己的内存。 所以我主要是从检查一些简短的循环列表开始,因为它们不会被垃圾回收器拾取。
这绝不是详尽的build议。 但是,在避免未来内存泄漏(循环)的思想写作时要记住的第一件事是确保任何接受对callback的引用的东西都应该将该callback存储为弱引用。
要检测和定位长时间运行的进程的内存泄漏,例如在生产环境中,现在可以使用stackimpact 。 它使用下面的tracemalloc 。 更多信息在这个职位 。
Tracemalloc模块是作为Python 3.4开始的内置模块集成的,显然,它也可以作为第三方库的Python以前版本使用(虽然没有经过testing)。
该模块能够输出分配最多内存的精确文件和行。 恕我直言,这种信息比每种types的分配实例的数量都要宝贵得多(最终99%的元组是99%的元组,这是一个线索,但在大多数情况下几乎没有帮助)。
我build议你使用tracemalloc和pyrasite结合使用。 10次中的9次,运行pyrasite-shell中的前10个片段会给你足够的信息和提示,以在10分钟内修复泄漏。 然而,如果你仍然无法find泄漏的原因,pyrasite-shell与这个线程中提到的其他工具结合起来可能会给你更多的提示。 你也应该看看pyrasite提供的所有额外帮助器(比如内存查看器)。
就最佳实践而言,请留意recursion函数。 在我的情况下,我遇到了recursion问题(在那里不需要)。 我正在做的一个简单的例子:
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()
以这种recursion的方式操作不会触发垃圾回收和清除function的遗留,所以每次通过内存使用量都在不断增长和增长。
我的解决scheme是将recursion调用从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()