web-dev-qa-db-ja.com

本番システムのPythonプロセスでメモリを使用しているものを見つけるにはどうすればよいですか?

私の本番システムでは、開発環境で再現できなかったメモリリークが発生することがあります。 Pythonメモリプロファイラー (具体的にはHeapy)を使用して開発環境である程度の成功を収めましたが、再現できないものについては役に立たず、インストルメントするのは気が進まないHeapyを使用した本番システムは、処理に時間がかかり、スレッド化されたリモートインターフェイスがサーバーで適切に機能しないためです。

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

39
keturn

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__属性を持つオブジェクトからのデータのみを保存していることに注意してください。これは、これらが私が気にする唯一のオブジェクトだからです。オブジェクトの完全なリストを保存することは可能ですが、他の属性の選択には注意が必要です。また、各オブジェクトのリファラーの取得が非常に遅いことがわかったため、リファラーのみを保存することにしました。とにかく、クラッシュ後、結果のpickle化されたデータは次のように読み戻すことができます。

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)
33
gerdemb

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

5
Brett

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

私はこれを行う必要がなかったので、100%うまくいくかどうかはわかりませんが、おそらくポインタが役立つでしょう。

3
Alex Coventry

pythonインタプリタの状態全体をダンプして復元する方法がわかりません。他の誰かがアイデアを持っている場合に備えて、この回答を監視しておくと便利です。

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

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

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

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

参照カウントのデバッグ

2
joeld

メリアス 有望に見えます:

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

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

2
keturn

gc module には、ガベージコレクターが到達できないが解放できないことがわかったすべてのオブジェクトのリストや、追跡されているすべてのオブジェクトのリストなど、役立つ機能がいくつかあります。

オブジェクトがリークする可能性がある疑いがある場合は、 weakref モジュールを使用すると、オブジェクトが収集されているかどうか/いつ収集されているかを確認できます。

1
Torsten Marek