Pythonのメモリリーク[終了]


180

実行時間の長いスクリプトを使用しています。十分に長く実行すると、システムのメモリがすべて消費されます。

スクリプトの詳細には触れませんが、2つの質問があります。

  1. リークの発生を防ぐのに役立つ「ベストプラクティス」はありますか?
  2. Pythonでメモリリークをデバッグするにはどのようなテクニックがありますか?

5
私が見つけたこのレシピは役に立ちます。
デビッドシャイン

あまりにも多くのデータが出力されて役に立たない
Casebash

1
@Casebash:その関数が何かを出力する場合、あなたは真剣にそれを間違っています。__del__サイクルを除いて参照されなくなったメソッドを持つオブジェクトがリストされます。の問題のため、サイクルを中断することはできません__del__。修理する!
Helmut Grohne、2011年

回答:



83

前述のほとんどのオプションを試してみましたが、この小さくて直感的なパッケージが最適であることがわかりました: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のドキュメント、特にセクション「メモリリークの特定」を確認してください。


5
SLOWにpymplerなる可能性があることに注意してください。半リアルタイムで何かをしている場合、アプリケーションのパフォーマンスが完全に低下する可能性があります。
フェイクネーム

@sebpiq不思議なことに、同じことが私にも起こります... なぜこれが起こっているのか何か分かりますか?ソースコードをざっと見ても、実際の洞察は得られませんでした。
linusg

25

作成したmem_topツールをお勧めします

同様の問題を解決するのに役立ちました

Pythonプログラムのメモリリークの上位の容疑者を即座に表示します


1
それは本当です...しかし、それは使用法/結果の説明の方法をほとんど提供しません
me_

@me_、このツールには「使用法」と「結果の説明」の両方のセクションが記載されています。「refsはオブジェクトからの参照の数、typesはこのタイプのオブジェクトの数、bytesはオブジェクトのサイズ」などの説明を追加する必要があります-これを文書化するのはあまりにも明白ではないでしょうか?
デニスリジコフ2018

ツールの使用法のドキュメントでは、「時々:logging.debug(mem_top())」という1行が表示されますが、結果の説明は、コンテキストのない作成者の実際のエラー追跡エクスペリエンスです...これは、技術仕様ではありません開発者が彼らが見ているものを正確に...私はあなたの答えをノックしていません...それは請求されたような高レベルの容疑者を示しています...それは使用の結果を完全に理解するための適切なドキュメントを提供しません...例えば、「結果の説明」出力で、「GearmanJobRequest」が明らかに問題である理由 理由の説明はありません...
me_

1
私はうっかりあなたのツールをノックしていると思います、あなたは作者です...攻撃は意図されていません...
me_

6
@me_、「使用方法」に次のステップを追加し、「カウンター」セクションを追加し、ギアマンが実際の例で疑わしい理由の説明を追加し、コードに「mem_top()」の各オプションパラメーターを文書化しました。これをすべてv0.1.7としてアップロードしました-他に改善できる点があるかどうか確認してください。ありがとうございました!)
デニスリジコフ2018

18

Tracemallocモジュールは、Python 3.4以降の組み込みモジュールとして統合されましたが、以前のバージョンのPythonでもサードパーティライブラリとして使用できます(ただし、テストはしていません)。

このモジュールは、最も多くのメモリを割り当てた正確なファイルと行を出力できます。私見、この情報は、各タイプに割り当てられたインスタンスの数よりもはるかに価値があります(これは、99%の時間に多くのタプルが発生することになる手がかりですが、ほとんどの場合、ほとんど役に立ちません)。

ピラサイトと組み合わせてtracemallocを使用することをお勧めします。10回のうち9回、ピラサイトシェル上位10個のスニペットを実行すると、10分以内にリークを修正するための十分な情報とヒントが得られます。それでもリークの原因を特定できない場合は、このスレッドで言及されている他のツールと組み合わせてピラサイトシェルを使用すると、さらにヒントが得られるでしょう。また、ピラサイトによって提供されるすべての追加のヘルパー(メモリビューアなど)も確認する必要があります。



12

特にグローバルデータまたは静的データ(長期データ)を確認する必要があります。

このデータが無制限に増加すると、Pythonでも問題が発生する可能性があります。

ガベージコレクターは、参照されなくなったデータのみを収集できます。ただし、静的データは解放する必要があるデータ要素をフックアップできます。

別の問題はメモリサイクルである可能性がありますが、少なくとも理論的には、ガベージコレクターはサイクルを見つけて排除する必要があります。

どのような種類の長期データが特に厄介ですか?リストや辞書をよく見てください。制限なく拡張できます。辞書では、辞書にアクセスすると、辞書内のキーの数があまり目立たなくなる可能性があるため、問題が発生することはないかもしれません...



4

ベストプラクティスについては、再帰的な関数に注意してください。私の場合、再帰の問題に遭遇しました(必要はありませんでした)。私がやっていたことの簡単な例:

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()

7
この方法で再帰を使用しても、Pythonは末尾呼び出しを最適化しないため、再帰の深さの制限に達した場合は失敗します。デフォルトでは、これは1000回の再帰呼び出しです。
リーライアン

3

Pythonのメモリリークの「ベストプラクティス」については不明ですが、Pythonはガベージコレクターによって自身のメモリをクリアする必要があります。したがって、主に、ガベージコレクターによってピックアップされない短いリストの循環リストを確認することから始めます。


3
または、永久に保持されているオブジェクトへの参照など
matt b

3
永遠に保管されている循環リストとオブジェクトの例を教えていただけますか?
ダニエル

2

これは決して完全なアドバイスではありません。ただし、将来のメモリリーク(ループ)を回避することを念頭に置いて作成する際に留意すべき最も重要なことは、コールバックへの参照を受け入れるものはすべて、そのコールバックを弱い参照として格納する必要があることを確認することです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.