50

運用システムで、開発環境では再現できないメモリ リークが発生することがあります。私はPython メモリ プロファイラー(具体的には Heapy) を使用して開発環境である程度の成功を収めましたが、再現できないものには役に立ちません。その作業を行うにはしばらく時間がかかり、そのスレッド化されたリモート インターフェイスはサーバーでうまく機能しません。

私が欲しいと思うのは、実稼働 Python プロセス (または少なくとも gc.get_objects) のスナップショットをダンプし、それをオフラインで分析してメモリを使用している場所を確認する方法です。 このような Python プロセスのコア ダンプを取得するにはどうすればよいですか? 取得したら、それを使って何か便利なことをするにはどうすればよいですか?

4

7 に答える 7

36

Python のgcガベージ コレクター インターフェイスを使用sys.getsizeof()すると、すべての Python オブジェクトとそのサイズをダンプできます。メモリリークをトラブルシューティングするために本番環境で使用しているコードは次のとおりです。

rss = psutil.Process(os.getpid()).get_memory_info().rss
# Dump variables if using more than 100MB of memory
if rss > 100 * 1024 * 1024:
    memory_dump()
    os.abort()

def memory_dump():
    dump = open("memory.pickle", 'wb')
    xs = []
    for obj in gc.get_objects():
        i = id(obj)
        size = sys.getsizeof(obj, 0)
        #    referrers = [id(o) for o in gc.get_referrers(obj) if hasattr(o, '__class__')]
        referents = [id(o) for o in gc.get_referents(obj) if hasattr(o, '__class__')]
        if hasattr(obj, '__class__'):
            cls = str(obj.__class__)
            xs.append({'id': i, 'class': cls, 'size': size, 'referents': referents})
    cPickle.dump(xs, dump)

属性を持つオブジェクトからのデータのみを保存していることに注意してください__class__。これは、私が気にする唯一のオブジェクトであるためです。オブジェクトの完全なリストを保存できるはずですが、他の属性の選択には注意が必要です。また、各オブジェクトのリファラーを取得するのが非常に遅いことがわかったので、リファレントのみを保存することにしました。とにかく、クラッシュの後、結果のピクルされたデータは次のように読み戻すことができます:

with open("memory.pickle", 'rb') as dump:
    objs = cPickle.load(dump)

2017-11-15 追加

Python 3.6 バージョンは次のとおりです。

import gc
import sys
import _pickle as cPickle

def memory_dump():
    with open("memory.pickle", 'wb') as dump:
        xs = []
        for obj in gc.get_objects():
            i = id(obj)
            size = sys.getsizeof(obj, 0)
            #    referrers = [id(o) for o in gc.get_referrers(obj) if hasattr(o, '__class__')]
            referents = [id(o) for o in gc.get_referents(obj) if hasattr(o, '__class__')]
            if hasattr(obj, '__class__'):
                cls = str(obj.__class__)
                xs.append({'id': i, 'class': cls, 'size': size, 'referents': referents})
        cPickle.dump(xs, dump)
于 2012-03-05T13:56:17.080 に答える
5

本番サイトでトラフィックを(ログを介して)記録し、Pythonメモリデバッガーを備えた開発サーバーで再生できますか?(私はドーザーをお勧めします:http://pypi.python.org/pypi/Dozer

于 2008-09-26T23:41:01.297 に答える
4

あなたのプログラムが core をダンプするようにしてから、 gdbを使用して十分に類似したボックスにプログラムのインスタンスを複製します。gdb 内で Python プログラムをデバッグするのに役立つ特別なマクロがありますが、プログラムで同時にリモート シェルを提供できる場合は、プログラムの実行を続行し、Python でクエリを実行できます。

私はこれを行う必要がなかったので、100% うまくいくとは確信していませんが、おそらくポインターは役に立ちます。

于 2008-09-27T00:11:08.587 に答える
2

Python インタープリターの状態全体をダンプして復元する方法がわかりません。他の誰かがアイデアを持っている場合に備えて、この回答に注目します。

メモリがリークしている場所がわかっている場合は、オブジェクトの参照カウントのチェックを追加できます。例えば:

x = SomeObject()
... later ...
oldRefCount = sys.getrefcount( x )
suspiciousFunction( x )
if (oldRefCount != sys.getrefcount(x)):
    print "Possible memory leak..."

アプリにとって妥当な数値よりも高い参照カウントを確認することもできます。さらに進めるには、Python インタープリターを変更して、Py_INCREFおよびPy_DECREFマクロを独自のものに置き換えることで、これらの種類のチェックを行うことができます。ただし、これは本番アプリでは少し危険かもしれません。

これは、これらの種類のデバッグに関する詳細情報を含むエッセイです。プラグインの作成者向けですが、ほとんどが適用されます。

参照カウントのデバッグ

于 2008-09-26T21:37:03.170 に答える
2

Meliaeは有望に見えます:

このプロジェクトは、メモリがどのように割り当てられているかを理解しようとする点で、('guppy' プロジェクトの) heapy に似ています。

現在、その主な違いは、メモリ消費の実際のスキャンから、メモリ消費の要約統計などを計算するタスクを分割することです。これは、プロセスが大量のメモリ (1GB など) を消費しているときに、プロセスで何が起こっているのかを把握したいことがよくあるためです。また、Python オブジェクトのメモリ消費を分析しようとしているときに Python オブジェクトを割り当てないため、スキャナーを大幅に簡素化することもできます。

于 2010-01-31T00:16:42.393 に答える
2

このgcモジュールには、ガベージ コレクターが到達不能で解放できないことがわかったすべてのオブジェクトの一覧表示や、追跡されているすべてのオブジェクトの一覧表示など、便利な機能がいくつかあります。

どのオブジェクトがリークする疑いがある場合は、weakrefモジュールを使用すると、オブジェクトが収集されたかどうか、いつ収集されたかを調べることができます。

于 2008-09-26T21:45:10.623 に答える