Python: Memory leak debugging
我有一个小的多线程脚本在Django中运行,随着时间的推移,它开始使用越来越多的内存。把它放一整天就可以吃掉6GB的内存,然后我开始交换。
根据http://www.lshift.net/blog/2008/11/14/tracing-python-memory-leaks,我认为这是最常见的类型(仅使用800m内存):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | (Pdb) objgraph.show_most_common_types(limit=20) dict 43065 tuple 28274 function 7335 list 6157 NavigableString 3479 instance 2454 cell 1256 weakref 974 wrapper_descriptor 836 builtin_function_or_method 766 type 742 getset_descriptor 562 module 423 method_descriptor 373 classobj 256 instancemethod 255 member_descriptor 218 property 185 Comment 183 __proxy__ 155 |
没有什么奇怪的。我现在应该做什么来帮助调试内存问题?
更新:尝试一些人们推荐的东西。我用了一整夜的时间运行程序,当我工作时,使用了50%*8g==4G的RAM。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | (Pdb) from pympler import muppy (Pdb) muppy.print_summary() types | # objects | total size ========================================== | =========== | ============ unicode | 210997 | 97.64 MB list | 1547 | 88.29 MB dict | 41630 | 13.21 MB set | 50 | 8.02 MB str | 109360 | 7.11 MB tuple | 27898 | 2.29 MB code | 6907 | 1.16 MB type | 760 | 653.12 KB weakref | 1014 | 87.14 KB int | 3552 | 83.25 KB function (__wrapper__) | 702 | 82.27 KB wrapper_descriptor | 998 | 77.97 KB cell | 1357 | 74.21 KB <class 'pympler.asizeof.asizeof._Claskey | 1113 | 69.56 KB function (__init__) | 574 | 67.27 KB |
号
这不等于4G,也不真正给我任何大数据结构去修复。unicode来自一组"done"节点,列表看起来就像是随机的
我没有使用Guppy,因为它需要C扩展,而且我没有根,所以构建起来会很痛苦。
没有一个objecti使用的是have-
请参阅http://opensourcehacker.com/2008/03/07/debugging-django-memory-leak-with-trackrefs-and-guppy/。简短回答:如果您运行的是Django,但不是基于Web请求的格式,则需要手动运行
在settings.py中debug=false吗?
如果不是这样,Django会很高兴地存储您所做的所有SQL查询,这些查询加起来就是。
您尝试过gc.set_debug()吗?
你需要问自己一些简单的问题:
- 我是否将对象与
__del__ 方法一起使用?我真的,明确地,需要他们吗? - 我能在代码中得到参考周期吗?我们不能在摆脱这些物体之前打破这些圆圈吗?
看,主要问题是包含
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import gc class A(object): def __del__(self): print 'a deleted' if hasattr(self, 'b'): delattr(self, 'b') class B(object): def __init__(self, a): self.a = a def __del__(self): print 'b deleted' del self.a def createcycle(): a = A() b = B(a) a.b = b return a, b gc.set_debug(gc.DEBUG_LEAK) a, b = createcycle() # remove references del a, b # prints: ## gc: uncollectable <A 0x...> ## gc: uncollectable <B 0x...> ## gc: uncollectable <dict 0x...> ## gc: uncollectable <dict 0x...> gc.collect() # to solve this we break explicitely the cycles: a, b = createcycle() del a.b del a, b # objects are removed correctly: ## a deleted ## b deleted gc.collect() |
我真的鼓励您标记正在应用程序中循环的对象/概念,并关注它们的生命周期:当您不再需要它们时,我们是否有任何引用它的内容?
即使对于没有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import gc # class without destructor class A(object): pass def createcycle(): # a -> b -> c # ^ | # ^<--<--<--| a = A() b = A() a.next = b c = A() b.next = c c.next = a return a, b, b gc.set_debug(gc.DEBUG_LEAK) a, b, c = createcycle() # since we have no __del__ methods, gc is able to collect the cycle: del a, b, c # no panic message, everything is collectable: ##gc: collectable <A 0x...> ##gc: collectable <A 0x...> ##gc: collectable <dict 0x...> ##gc: collectable <A 0x...> ##gc: collectable <dict 0x...> ##gc: collectable <dict 0x...> gc.collect() a, b, c = createcycle() # but as long as we keep an exterior ref to the cycle...: seen = dict() seen[a] = True # delete the cycle del a, b, c # nothing is collected gc.collect() |
号
如果您必须使用"seen"(如字典或历史),请注意只保留所需的实际数据,而不保留对它的外部引用。
我现在对
看看这篇来自NedBatchelder的优秀博客文章,了解他们是如何追踪到惠普Tabblo中的真正内存泄漏的。经典而值得一读。
你用分机吗?它们是内存泄漏的好地方,不会被Python工具跟踪。
我认为你应该使用不同的工具。显然,您得到的统计信息只是关于GC对象(即可能参与循环的对象);最明显的是,它缺少字符串。
我建议使用pympler;这将为您提供更详细的统计信息。
试试古皮。
基本上,你需要更多的信息或者能够提取一些。Guppy甚至提供了数据的图形化表示。