2013-06-04 16 views
6

私のpythonアプリケーションの1つが、メモリ使用量が着実に増加していると判断してメモリをリークするようです。私の仮説は、これを避けるために最善の努力をしていたにもかかわらず、どこかで循環参照である。問題を特定するために、私は手動で到達不能な項目をチェックする方法を探しています。ツールは純粋にデバッグを目的としています。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のインポートに関する問題を説明することができますか? は問題を解決する方法を提案できますか?

付け足し:

それがコードは以下の意図に近い行うように見える:GCによって提供されるよう

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

しかし、デバッグのためにI型/ IDオーバーリッチ文字列表現を好みます。さらに、以前のアプローチの欠陥を理解し、gcモジュールについて何かを学びたいと思います。あなたの助けを鑑賞

Gertjan

更新06/05:

地元の人々()は、直前に呼び出された場合を除き、私は、最初の実装は、任意の到達不能項目を報告しなかった事態に遭遇しました戻り値を破棄します。これがgcのオブジェクト追跡にどのように影響するかを理解していないと、私はさらに混乱します。私はこの問題を示す小さな例を作るのがどれくらい簡単かはわかりませんが、要求があればそれを撃つことができます。

+0

私はこれをスキミングしましたが、 'gc.DEBUG_SAVEALL'を誤解しているかもしれません。解放されたオブジェクトを' gc.garbage'に(単に解放するのではなく)追加するためのものです。 – blueyed

答えて

0

最後に私がこのような必要性を感じたとき、私はobjgraph moduleを使って良い結果を得ました。 gc moduleから簡単に入手できるよりもはるかに正確な情報を提供します。残念ながら、そのコードの使用方法を示すコードはありません。

1つの場所は、Cコードライブラリによって割り当てられたメモリ内にあります。たとえば、プロジェクトがPILを使用している場合、Pythonオブジェクトを適切に解放してCデータをバックアップしているため、メモリをリークするのは非常に簡単です。このようなオブジェクトを適切に閉じる方法は、Cのバックエンドのモジュールに依存します。

+0

こんにちはSamantha、ありがとう、はい私はobjgraphがオブジェクト参照をトレースするための素晴らしいツールであることに同意します。しかし、私は、私の例では「A」が表示されるように、それを使って、手の届かない項目のリストを私に与える方法を理解できませんでした。 get_leaking_objectsを使った実験は失敗しました。彼のブログ記事では、Mariusはグローバルオブジェクトからのリファレンスの例によるメモリリークについて説明しています。したがって、objgraphは循環参照のトレースには適していないと考えられました。その結論が間違っている場合、私はこれらが明らかにされる方法を学ぶことに最も興味があります。 – gertjan

関連する問題