我有一个在django中运行的小型多线程脚本,随着时间的推移,它开始使用越来越多的内存。离开它一整天都会吃掉大约6GB的内存,然后我开始交换。
关注http://www.lshift.net/blog/2008/11/14/tracing-python-memory-leaks我认为这是最常见的类型(只使用了800M的内存):
(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。
(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来自“完成”节点的set(),列表看起来就像是随机weakref
s。
我没有使用guppy,因为它需要一个C扩展而且我没有root,因此构建起来会很痛苦。
没有一个objectI正在使用__del__
方法,并查看库,它看起来不像django,也不像python-mysqldb。还有其他想法吗?
答案 0 :(得分:31)
见http://opensourcehacker.com/2008/03/07/debugging-django-memory-leak-with-trackrefs-and-guppy/。简短回答:如果您正在运行django而不是基于Web请求的格式,则需要手动运行db.reset_queries()
(当然,正如其他人所提到的那样,DEBUG = False)。在网络请求之后,Django会自动reset_queries()
,但在您的格式中,这种情况从未发生过。
答案 1 :(得分:19)
settings.py中的DEBUG = False吗?
如果不是,Django会愉快地存储你所做的所有SQL查询,这些查询会加起来。
答案 2 :(得分:6)
您是否尝试过gc.set_debug()?
你需要问自己一些简单的问题:
__del__
方法的对象吗?我绝对,毫不含糊地需要它们吗?请参阅,主要问题是包含__del__
方法的对象循环:
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()
我真的鼓励你标记在你的应用程序中循环的对象/概念并关注它们的生命周期:当你不再需要它们时,我们是否有任何引用它的东西?
即使没有__del__
方法的周期,我们也会遇到问题:
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()
如果您必须使用“看似”字典或历史记录,请注意您只保留所需的实际数据,并且不要外部引用它。
我对set_debug
感到有点失望,我希望它可以配置为输出数据而不是stderr,但希望that should change soon。
答案 3 :(得分:5)
请参阅this excellent blog post from Ned Batchelder,了解他们如何追踪HP Tabblo中的实际内存泄漏情况。经典且值得一读。
答案 4 :(得分:1)
我认为你应该使用不同的工具。显然,你得到的统计数据只是关于GC对象(即可能参与循环的对象);最值得注意的是,它缺乏字符串。
我建议使用Pympler;这应该为您提供更详细的统计数据。
答案 5 :(得分:1)
你使用任何扩展名吗?它们是内存泄漏的绝佳场所,不会被python工具跟踪。
答案 6 :(得分:0)
尝试Guppy。
基本上,您需要更多信息或能够提取一些信息。 Guppy甚至提供数据的图形表示。