長時間実行するスクリプトを使用すると、十分に長く実行すると、システム上のすべてのメモリが消費されます。
スクリプトについて詳しく説明しませんが、2つの質問があります。
この記事をご覧ください: Tracing python memory leaks
また、 garbage collection module には実際にデバッグフラグを設定できることに注意してください。 set_debug
関数を見てください。さらに、呼び出し後に作成されたオブジェクトのタイプを判別するには、 Gnibblerによるこのコード を参照してください。
前述のほとんどのオプションを試してみましたが、この小さく直感的なパッケージが最適であることがわかりました: pympler
ガベージコレクションされていないオブジェクトをトレースするのは非常に簡単です。次の小さな例を確認してください。
pip install pympler
経由でパッケージをインストールします
from pympler.tracker import SummaryTracker
tracker = SummaryTracker()
# ... some code you want to investigate ...
tracker.print_diff()
出力には、追加されたすべてのオブジェクトと、それらが消費したメモリが表示されます。
サンプル出力:
types | # objects | total size
====================================== | =========== | ============
list | 1095 | 160.78 KB
str | 1093 | 66.33 KB
int | 120 | 2.81 KB
dict | 3 | 840 B
frame (codename: create_summary) | 1 | 560 B
frame (codename: print_diff) | 1 | 480 B
このパッケージは、さらに多くの機能を提供します。 pymplerのドキュメント 、特に メモリリークの特定 セクションを確認してください。
mem_top ツールをお勧めします。
それは私が同様の問題を解決するのに役立ちました。
Pythonプログラムのメモリリークの上位の容疑者を即座に表示します。
Tracemallocモジュール はPython 3.4からビルトインモジュールとして統合され、明らかにPythonの以前のバージョンでも利用可能です サードパーティライブラリ (ただし、テストしていません)。
このモジュールは、最も多くのメモリを割り当てた正確なファイルと行を出力できます。私見、この情報は各タイプに割り当てられたインスタンスの数よりも無限に価値があります(99%の時間に多くのタプルになりますが、これは手掛かりですが、ほとんどの場合ほとんど役に立ちません)。
Tracemallocを pyrasite と組み合わせて使用することをお勧めします。 10回のうち9回、 pyrasite-Shell で top 10 snippet を実行すると、10分以内にリークを修正するための十分な情報とヒントが得られます。それでも、リークの原因がまだ見つからない場合は、このスレッドで言及されている他のツールと組み合わせてpyrasite-Shellを使用すると、おそらくさらにヒントが得られます。また、ピラサイトが提供するすべての追加のヘルパー(メモリビューアーなど)も確認する必要があります。
特に、グローバルデータまたは静的データ(長寿命データ)を確認する必要があります。
このデータが制限なしに増加すると、Pythonで問題が発生することもあります。
ガベージコレクターは、もはや参照されていないデータのみを収集できます。ただし、静的データは、解放する必要があるデータ要素を接続できます。
別の問題はメモリサイクルである可能性がありますが、少なくとも理論的には、ガベージコレクタはサイクルを見つけて排除する必要があります。
どんな種類の長寿命データが特に面倒ですか?リストや辞書をよく見てください-制限なしに成長できます。辞書では、辞書にアクセスするときに辞書のキーの数があまり見えないため、問題が発生することさえないかもしれません...
長時間実行されているプロセスのメモリリークを検出して特定する実稼働環境では、 stackimpact を使用できるようになりました。下で tracemalloc を使用します。詳細は この投稿 で。
Pythonのメモリリークの「ベストプラクティス」についてはわかりませんが、pythonはガベージコレクタによって自身のメモリをクリアする必要があります。だから、私はそれらがガベージコレクタによってピックアップされないので、主にいくつかの短いの循環リストをチェックすることから始めます。
ベストプラクティスに関しては、再帰関数に注意してください。私の場合、再帰の問題に遭遇しました(必要はありませんでした)。私がやっていることの簡単な例:
def my_function():
# lots of memory intensive operations
# like operating on images or huge dictionaries and lists
.....
my_flag = True
if my_flag: # restart the function if a certain flag is true
my_function()
def main():
my_function()
この再帰的な方法で操作すると、ガベージコレクションがトリガーされず、関数の残りがクリアされないため、メモリ使用量が増加するたびに増加します。
私の解決策は、再帰呼び出しをmy_function()から引き出し、main()がいつそれを呼び出すかを処理することでした。このようにして、関数は自然に終了し、自動的にクリーンアップされます。
def my_function():
# lots of memory intensive operations
# like operating on images or huge dictionaries and lists
.....
my_flag = True
.....
return my_flag
def main():
result = my_function()
if result:
my_function()
これは決して包括的なアドバイスではありません。しかし、将来のメモリリーク(ループ)を回避することを考えて記述する際に留意する第1のことは、コールバックへの参照を受け入れるものはすべて、そのコールバックを弱い参照として保存することを確認することです。