次の例には、メモリ使用量に関するいくつかの関連する質問があります。
インタプリタで実行すると、
foo = ['bar' for _ in xrange(10000000)]
私のマシンで使用される実際のメモリは80.9mb
になります。そして私は・・・それから私は、
del foo
実メモリは低下しますが、30.4mb
のみです。インタープリターは4.4mb
ベースラインを使用するので、OSに26mb
のメモリーを解放しないことの利点は何ですか?それは、Pythonが「前もって計画している」ためであり、そのメモリを再び使用する可能性があると考えているからでしょうか。
特に50.5mb
をリリースするのはなぜですか-ベースのリリース量はいくらですか?
Pythonが使用されたすべてのメモリを強制的に解放する方法はありますか(そのメモリを再び使用しないことがわかっている場合)。
NOTEこの質問は Pythonでメモリを明示的に解放するにはどうすればよいですか? とは異なりますインタプリタがガベージコレクションを介してオブジェクトを解放した後(gc.collect
を使用するかどうかにかかわらず)のベースラインからのメモリ使用量。
ヒープに割り当てられたメモリは、最高水準点の影響を受ける可能性があります。これは、8バイトの倍数(最大256バイト(3.3では512バイト))の割り当てサイズに分類された4つのKiBプールに小さなオブジェクト(PyObject_Malloc
)を割り当てるためのPythonの内部最適化によって複雑になります。プール自体は256 KiBアリーナにあるため、1つのプール内の1つのブロックのみが使用される場合、256 KiBアリーナ全体は解放されません。 Python 3.3では、小さなオブジェクトアロケーターは、ヒープではなく匿名のメモリマップを使用するように切り替えられたため、メモリを解放する際のパフォーマンスが向上します。
さらに、組み込み型は、小さなオブジェクトアロケーターを使用する場合と使用しない場合がある、以前に割り当てられたオブジェクトの空きリストを保持します。 int
型は、独自のメモリが割り当てられた空きリストを保持し、クリアするにはPyInt_ClearFreeList()
を呼び出す必要があります。これは、完全なgc.collect
を実行することで間接的に呼び出すことができます。
このように試して、何が得られるか教えてください。 psutil.Process.memory_info のリンクを次に示します。
import os
import gc
import psutil
proc = psutil.Process(os.getpid())
gc.collect()
mem0 = proc.get_memory_info().rss
# create approx. 10**7 int objects and pointers
foo = ['abc' for x in range(10**7)]
mem1 = proc.get_memory_info().rss
# unreference, including x == 9999999
del foo, x
mem2 = proc.get_memory_info().rss
# collect() calls PyInt_ClearFreeList()
# or use ctypes: pythonapi.PyInt_ClearFreeList()
gc.collect()
mem3 = proc.get_memory_info().rss
pd = lambda x2, x1: 100.0 * (x2 - x1) / mem0
print "Allocation: %0.2f%%" % pd(mem1, mem0)
print "Unreference: %0.2f%%" % pd(mem2, mem1)
print "Collect: %0.2f%%" % pd(mem3, mem2)
print "Overall: %0.2f%%" % pd(mem3, mem0)
出力:
Allocation: 3034.36%
Unreference: -752.39%
Collect: -2279.74%
Overall: 2.23%
編集:
プロセスVMサイズに関連する測定に切り替えて、システム内の他のプロセスの影響を排除しました。
Cランタイム(glibc、msvcrtなど)は、上部の連続した空き領域が一定、動的、または構成可能なしきい値に達すると、ヒープを縮小します。 glibcでは、 mallopt
(M_TRIM_THRESHOLD)でこれを調整できます。これを考えると、ヒープがfree
のブロックよりもさらに多く、さらにはもっと多く収縮しても驚くことではありません。
3.xでは、range
はリストを作成しません。したがって、上記のテストでは1,000万個のint
オブジェクトは作成されません。たとえそれが行われたとしても、3.xのint
型は基本的に2.x long
であり、フリーリストを実装していません。
ここで本当に気になる質問は次のとおりです。
Pythonが使用されたすべてのメモリを強制的に解放する方法はありますか(そのメモリを再び使用しないことがわかっている場合)。
いいえ、ありません。しかし、簡単な回避策があります:子プロセス。
500MBの一時ストレージが5分間必要であるが、その後さらに2時間実行する必要があり、それ以上多くのメモリにアクセスしない場合、子プロセスを生成してメモリ集約型の作業を行います。子プロセスがなくなると、メモリが解放されます。
これは完全に簡単で無料ではありませんが、非常に簡単で安価です。通常、取引に値するほど十分です。
第一に、子プロセスを作成する最も簡単な方法は concurrent.futures
(または3.1以前では、PyPIで futures
バックポート)を使用することです:
with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
result = executor.submit(func, *args, **kwargs).result()
もう少し制御が必要な場合は、 multiprocessing
モジュールを使用します。
費用は次のとおりです。
mmap
pedまたはその他; multiprocessing
の共有メモリAPI;など)。struct
- ableまたは理想的にはctypes
- able)。eryksunは質問#1に答え、質問#3(元の#4)には答えましたが、質問#2に答えましょう。
特に50.5mbをリリースするのはなぜですか-ベースでリリースされる量はいくらですか?
最終的には、Pythonとmalloc
内の一連の偶然の一致が非常に困難です。
まず、メモリの測定方法によっては、実際にメモリにマップされたページのみを測定している場合があります。その場合、ページャーによってページがスワップアウトされるたびに、メモリは解放されていなくても「解放済み」として表示されます。
または、使用中のページを測定している可能性があります。これは、割り当てられているが決して触れられていないページ(Linuxのように楽観的に過剰に割り当てられているシステム)、割り当てられているがMADV_FREE
などのページなどをカウントする場合とカウントしない場合があります.
割り当てられたページを実際に測定している場合(実際にはあまり有用ではありませんが、あなたが求めているもののようです)、ページが実際に割り当て解除された場合、これが発生する可能性のある2つの状況:どちらかbrk
または同等のものを使用してデータセグメントを縮小した(最近では非常にまれです)か、またはmunmap
または同様のものを使用してマップセグメントを解放しました。 (理論的には、後者のマイナーバリアントもあります。マップされたセグメントの一部を解放する方法があります。たとえば、すぐにマップ解除するMAP_FIXED
セグメントをMADV_FREE
で盗む方法です。)
しかし、ほとんどのプログラムはメモリページから直接物を割り当てません。 malloc
スタイルのアロケーターを使用します。 free
を呼び出すと、たまたまマッピングの最後のライブオブジェクト(またはデータセグメントの最後のNページ)をfree
ingしている場合にのみ、アロケーターはページをOSにリリースできます。アプリケーションがこれを合理的に予測することも、事前に発生したことを検出することもできません。
CPythonはこれをさらに複雑にします。malloc
の上にカスタムメモリアロケーターの上にカスタム2レベルオブジェクトアロケーターがあります。 (詳細な説明については ソースコメント を参照してください。)その上、C APIレベルでさえPythonではなく、トップレベルオブジェクトをいつ制御するかさえも直接制御しません。割り当て解除。
それでは、オブジェクトを解放するとき、OSにメモリを解放するかどうかをどのようにして知るのでしょうか?まず、最後の参照(知らなかった内部参照を含む)をリリースしたことを知って、GCがその参照を解除できるようにする必要があります。 (他の実装とは異なり、少なくともCPythonは許可されるとすぐにオブジェクトの割り当てを解除します。)これは通常、次のレベルで少なくとも2つのことを割り当て解除します(たとえば、文字列の場合、PyString
オブジェクトと文字列を解放しますバッファ)。
doオブジェクトの割り当てを解除する場合、これにより次のレベルでオブジェクトストレージのブロックの割り当てが解除されるかどうかを知るには、オブジェクトアロケータの内部状態も知る必要があります実装方法として。 (ブロック内の最後のものの割り当てを解除しない限り、それは明らかに起こり得ません。それでも、それは起こらないかもしれません。)
オブジェクトストレージのブロックをdo割り当て解除すると、これがfree
呼び出しを引き起こすかどうかを知るために、PyMemアロケーターの内部状態とその実装方法を知る必要があります。 (繰り返しますが、malloc
ed領域内の最後の使用中ブロックの割り当てを解除する必要がありますが、それでも発生しない場合があります。)
あなたがdofree
malloc
ed領域である場合、これがmunmap
または同等(またはbrk
)を引き起こすかどうかを知るには、malloc
の内部状態とその方法を知る必要があります実装されました。そして、これは他とは異なり、高度にプラットフォーム固有です。 (繰り返しますが、通常、malloc
セグメント内の最後に使用中のmmap
の割り当てを解除する必要がありますが、それでも発生しない場合があります。)
そのため、なぜ50.5MBだけをリリースしたのかを理解したい場合は、ボトムアップでトレースする必要があります。 malloc
が1つ以上のfree
呼び出しを行ったときに、おそらくmalloc
が50.5mbのページのマッピングを解除したのはなぜですか(おそらく50.5mb以上)。プラットフォームのmmap
を読んでから、さまざまなテーブルとリストを調べて現在の状態を確認する必要があります。 (一部のプラットフォームでは、システムレベルの情報を使用することもあります。これは、システムのスナップショットを作成せずにキャプチャしてオフラインで検査することはほとんど不可能ですが、幸いなことにこれは通常問題ではありません。)上記の3つのレベルで同じことを行います。
したがって、質問に対する唯一の有用な答えは「理由」です。
リソースに制限のある(埋め込みなどの)開発を行っていない限り、これらの詳細を気にする必要はありません。
そして、もしあなたがリソース限定開発をしているなら、これらの詳細を知ることは役に立たない。これらのすべてのレベル、特にアプリケーションレベルで必要なメモリ(特に、間に1つのシンプルで十分に理解されたアプリケーション固有のゾーンアロケーターを使用)をエンドランする必要があります。
まず、Glanceをインストールすることをお勧めします。
Sudo apt-get install python-pip build-essential python-dev lm-sensors
Sudo pip install psutil logutils bottle batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard
Sudo pip install glances
次に、ターミナルで実行します!
glances
Pythonコードで、ファイルの先頭に次を追加します。
import os
import gc # Garbage Collector
「Big」変数(例:myBigVar)を使用した後、メモリを解放するには、次のpythonコードを記述します。
del myBigVar
gc.collect()
別のターミナルでpythonコードを実行し、「glances」ターミナルで、システムでメモリがどのように管理されているかを観察します。
がんばろう!
追伸DebianまたはUbuntuシステムで作業していると思います