2013-06-04 84 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