33

これが私のコードです:

from memory_profiler import profile

@profile
def mess_with_memory():
    huge_list = range(20000000)
    del huge_list
    print "why this kolaveri di?"

これは、インタープリターから実行したときの出力です。

行番号 メモリ使用量の増分 行の内容

 3      7.0 MiB      0.0 MiB   @profile
 4                             def mess_with_memory():
 5                             
 6    628.5 MiB    621.5 MiB       huge_list = range(20000000)
 7    476.0 MiB   -152.6 MiB       del huge_list
 8    476.0 MiB      0.0 MiB       print "why this kolaveri di"

出力に気付いた場合、巨大なリストを作成すると 621.5 MB が消費され、削除すると 152.6 MB が解放されました。docsを確認したところ、次のステートメントが見つかりました。

the statement del x removes the binding of x from the namespace referenced by the local scope

つまり、オブジェクト自体を削除したのではなく、バインドを解除しただけだと思います。しかし、バインド解除で何をしたので、多くのスペース (152.6 MB) が解放されました。誰かがここで何が起こっているのか説明してくれませんか?

4

1 に答える 1

49

Python はガベージ コレクション言語です。コードから値に到達できなくなった場合、その値は最終的に削除されます。

ご覧のdelとおり、ステートメントは変数のバインディングを削除します。変数は値ではなく、値の単なる名前です。

その変数がどこかの値への唯一の参照である場合、その値は最終的に削除されます。特に CPython では、ガベージ コレクターは参照カウントの上に構築されています。そのため、「最終的に」は「すぐに」を意味します。* 他の実装では、通常は「かなりすぐに」です。

ただし、同じ値への他の参照があった場合、それらの参照の 1 つを削除するだけでは ( del x、 、存在x = Noneするスコープを終了するxなどによって)、何もクリーンアップされません。**


ここで別の問題があります。memory_profilerモジュール(おそらくこれ)が実際に何を測定するのかはわかりませんが、説明(の使用について話しているpsutil)は、「外部」からメモリ使用量を測定しているように聞こえます。

Python がストレージを解放しても、オペレーティング システムに戻すとは限りません (通常はそうではありません)。「空きリスト」を複数のレベルで保持するため、OS に戻ってさらに要求する必要がある場合よりも、メモリをより迅速に再利用できます。最新のシステムでは、これが問題になることはめったにありません。ストレージが再び必要になった場合は、それがあったことは良いことです。そうしないと、他の誰かがそれを必要とするとすぐにページアウトされ、二度とページインされないため、害はほとんどありません.

(さらに、上記で「OS」と呼んだものは、実際には、ライブラリからコア C ライブラリを介してカーネル/ページャーに至るまで、複数のレベルで構成される抽象化であり、malloc通常、これらのレベルの少なくとも 1 つは、独自の空きリスト)。

内部の観点からメモリの使用状況を追跡したい場合は…まあ、それはかなり難しいです。tracemalloc新しいモジュールのおかげで、Python 3.4 でより簡単になりました。以前のバージョンで同じ種類の情報を取得しようとするさまざまなサードパーティ モジュール (たとえば、heapy/ guppyPymplermeliae) がありますが、さまざまなアロケーターから情報を取得し、その情報をガベージ コレクターに結び付けるのが非常に困難だったため、困難です。PEP 445より前のハード。


* 場合によっては値への参照がありますが、それ自体が到達不能な他の参照からのみであり、サイクル内にある可能性があります。ガベージコレクターに関する限り、それはまだ「到達不能」と見なされますが、参照カウントに関する限りではありません。そのため、CPython には「サイクル検出器」もあり、頻繁に実行され、相互に到達可能であるが他の誰からも到達不可能な値のサイクルを見つけてクリーンアップします。

** インタラクティブ コンソールでテストしている場合、追跡が困難な値への非表示の参照が存在する可能性があるため、最後の参照を削除していないのに削除したと考えるかもしれません。スクリプトでは、簡単ではないにしても、物事を理解することが常に可能であるべきです。デバッガーと同様に、モジュールが役立ちます。もちろん、どちらも隠し参照を追加する新しい方法も提供しますgc

于 2014-01-10T20:03:43.867 に答える