実行時間の長いスクリプトを使用しています。十分に長く実行すると、システムのメモリがすべて消費されます。
スクリプトの詳細には触れませんが、2つの質問があります。
- リークの発生を防ぐのに役立つ「ベストプラクティス」はありますか?
- Pythonでメモリリークをデバッグするにはどのようなテクニックがありますか?
__del__
サイクルを除いて参照されなくなったメソッドを持つオブジェクトがリストされます。の問題のため、サイクルを中断することはできません__del__
。修理する!
実行時間の長いスクリプトを使用しています。十分に長く実行すると、システムのメモリがすべて消費されます。
スクリプトの詳細には触れませんが、2つの質問があります。
__del__
サイクルを除いて参照されなくなったメソッドを持つオブジェクトがリストされます。の問題のため、サイクルを中断することはできません__del__
。修理する!
回答:
この記事をご覧ください:Pythonメモリリークのトレース
また、ガベージコレクションモジュールでは実際にデバッグフラグを設定できることに注意してください。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のドキュメント、特にセクション「メモリリークの特定」を確認してください。
pympler
なる可能性があることに注意してください。半リアルタイムで何かをしている場合、アプリケーションのパフォーマンスが完全に低下する可能性があります。
Tracemallocモジュールは、Python 3.4以降の組み込みモジュールとして統合されましたが、以前のバージョンのPythonでもサードパーティライブラリとして使用できます(ただし、テストはしていません)。
このモジュールは、最も多くのメモリを割り当てた正確なファイルと行を出力できます。私見、この情報は、各タイプに割り当てられたインスタンスの数よりもはるかに価値があります(これは、99%の時間に多くのタプルが発生することになる手がかりですが、ほとんどの場合、ほとんど役に立ちません)。
ピラサイトと組み合わせてtracemallocを使用することをお勧めします。10回のうち9回、ピラサイトシェルで上位10個のスニペットを実行すると、10分以内にリークを修正するための十分な情報とヒントが得られます。それでもリークの原因を特定できない場合は、このスレッドで言及されている他のツールと組み合わせてピラサイトシェルを使用すると、さらにヒントが得られるでしょう。また、ピラサイトによって提供されるすべての追加のヘルパー(メモリビューアなど)も確認する必要があります。
特にグローバルデータまたは静的データ(長期データ)を確認する必要があります。
このデータが無制限に増加すると、Pythonでも問題が発生する可能性があります。
ガベージコレクターは、参照されなくなったデータのみを収集できます。ただし、静的データは解放する必要があるデータ要素をフックアップできます。
別の問題はメモリサイクルである可能性がありますが、少なくとも理論的には、ガベージコレクターはサイクルを見つけて排除する必要があります。
どのような種類の長期データが特に厄介ですか?リストや辞書をよく見てください。制限なく拡張できます。辞書では、辞書にアクセスすると、辞書内のキーの数があまり目立たなくなる可能性があるため、問題が発生することはないかもしれません...
実稼働環境などで長時間実行されているプロセスのメモリリークを検出して特定するために、stackimpactを使用できるようになりました。その下でtracemallocを使用します。この投稿の詳細情報。
ベストプラクティスについては、再帰的な関数に注意してください。私の場合、再帰の問題に遭遇しました(必要はありませんでした)。私がやっていたことの簡単な例:
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()
これは決して完全なアドバイスではありません。ただし、将来のメモリリーク(ループ)を回避することを念頭に置いて作成する際に留意すべき最も重要なことは、コールバックへの参照を受け入れるものはすべて、そのコールバックを弱い参照として格納する必要があることを確認することです。