2013-06-04 44 views
6

从稳步增加的内存使用情况来看,我的一个python应用程序似乎泄漏了内存。我的假设是循环参考,尽管尽最大努力避免这种情况。为了找出问题,我正在研究如何手动检查无法访问的项目,这是一个纯粹用于调试的工具。了解Python的gc.garbage(用于跟踪内存泄漏)的问题

gc模块似乎能够进行必要的跟踪,并且我尝试了以下代码,旨在编译自上次调用后形成的不可缓存项目列表。第一次调用只是设置一个基本检查点,不会识别不可达的项目。

def unreachable(): 
    # first time setup 
    import gc 
    gc.set_threshold(0) # only manual sweeps 
    gc.set_debug(gc.DEBUG_SAVEALL) # keep unreachable items as garbage 
    gc.enable() # start gc if not yet running (is this necessary?) 
    # operation 
    if gc.collect() == 0: 
    return 'no unreachable items' 
    s = 'unreachable items:\n ' \ 
    + '\n '.join('[%d] %s' % item for item in enumerate(gc.garbage)) 
    _deep_purge_list(gc.garbage) # remove unreachable items 
    return s # return unreachable items as text 

在这里,_deep_purge_list旨在手动中断周期并删除对象。以下实现处理一些常见的情况,但不是靠近水密的。我的第一个问题与此有关,请看下。

def _deep_purge_list(garbage): 
    for item in garbage: 
    if isinstance(item, dict): 
     item.clear() 
    if isinstance(item, list): 
     del item[:] 
    try: 
     item.__dict__.clear() 
    except: 
     pass 
    del garbage[:] 

基于非常有限的测试,设置似乎正常工作。用下面的一个奇怪的现象发生

class A(object): 
    def __init__(self): 
    self.ref = self 

print unreachable() 
# no unreachable items 

A() 

print unreachable() 
# unreachable items: 
# [0] <__main__.A object at 0xb74579ac> 
# [1] {'ref': <__main__.A object at 0xb74579ac>} 

print unreachable() 
# no unreachable items 

但是:下列循环引用正确报告一次

print unreachable() 
# no unreachable items 

import numpy 

print unreachable() 
# unreachable items: 
# [0] (<type '_ctypes.Array'>,) 
# [1] {'__module__': 'numpy.ctypeslib', '__dict__': <attribute '__dict__' of 'c_long_Array_1' objects>, '__weakref__': <attribute '__weakref__' of 'c_long_Array_1' objects>, '_length_': 1, '_type_': <class 'ctypes.c_long'>, '__doc__': None} 
# [2] <class 'numpy.ctypeslib.c_long_Array_1'> 
# [3] <attribute '__dict__' of 'c_long_Array_1' objects> 
# [4] <attribute '__weakref__' of 'c_long_Array_1' objects> 
# [5] (<class 'numpy.ctypeslib.c_long_Array_1'>, <type '_ctypes.Array'>, <type '_ctypes._CData'>, <type 'object'>) 

print unreachable() 
# unreachable items: 
# [0] (<type '_ctypes.Array'>,) 
# [1] {} 
# [2] <class 'c_long_Array_1'> 
# [3] (<class 'c_long_Array_1'>, <type '_ctypes.Array'>, <type '_ctypes._CData'>, <type 'object'>) 

重复调用的回头率那最后的结果。导入后第一次调用不可访问时,不会发生此问题。然而,在这一点上,我没有理由相信这个问题是具体的,我的猜测是它暴露了我的方法中的缺陷。

我的问题:

  1. 是否有更好的方法来去除gc.garbage项目?理想情况下, 有一种方法可以让gc移除它们,就像它会(应该)在没有DEBUG_SAVEALL的情况下完成 一样?
  2. 任何人都可以解释numpy导入的问题,和/或 建议如何解决它?

有感:

看来,下面的代码执行接近预期:

def unreachable(): 
    import gc 
    gc.set_threshold(0) 
    gc.set_debug(gc.DEBUG_LEAK) 
    gc.enable() 
    print 'collecting {{{' 
    gc.collect() 
    print '}}} done' 

然而,对于调试我更喜欢类型/ ID丰富的字符串表示,通过GC提供。此外,我想了解我以前的方法中的缺陷,并了解一下gc模块。

欣赏你的帮助,

Gertjan

更新06/05:

我遇到了一个情况:第一次执行未报告任何不可达的项目,除非当地人()被调用之前刚到它(丢弃返回值)。不理解这可能会如何影响gc的对象跟踪,这让我更加困惑。我不确定构建一个能够证明这个问题的小例子是多么容易,但是如果需求需要它,我可以给它一个机会。

+0

我只是略过了这个,但你可能误解了'gc.DEBUG_SAVEALL':它意味着将被释放的对象追加到'gc.garbage'(而不是仅释放它们)。 – blueyed

回答

0

最后一次我有这样的需求,我结束了使用objgraph module达到良好效果。它提供的信息比直接从gc module轻松获得的信息要准确得多。不幸的是,我手边没有任何代码说明它的用法。

它掉下来的地方是由任何C代码库调用的内存分配的。例如,如果一个项目使用PIL,由于没有正确释放由C数据支持的python对象,泄漏内存非常容易。这是由C支持的模块相关如何正确关闭这些对象。

+0

嗨Samantha,谢谢,是的我同意objgraph是一个很好的跟踪对象引用的工具。然而,我无法弄清楚如何使用它给我一个不可升级项目的列表,以我的例子中出现'A'的方式; get_leaking_objects的实验不成功。 Marius在他的博客文章中通过全局对象引用的例子解释了内存泄漏,所以我被引导认为objgraph可能不适合于跟踪循环引用。如果这个结论是错误的,我最有兴趣了解如何发现这些结论。 – gertjan