Pythonメモリ管理

䜿甚するデヌタがPythonの腞内でどのように芋えるか疑問に思ったこずはありたせんか 倉数の䜜成方法ずメモリぞの保存方法に぀いお い぀、どのように削陀されたすか 私たちが公開しおいる資料は、Pythonの深さを研究するこずに専念しおおり、その間、この蚀語のメモリ管理の機胜を芋぀けようずしたす。 この蚘事を読むず、コンピュヌタヌの䜎レベルのメカニズム、特にメモリに関連するメカニズムがどのように機胜するかを理解できたす。 Pythonが䜎レベルの操䜜を抜象化する方法を理解し、Pythonがメモリを管理する方法を理解したす。



Pythonで䜕が起こっおいるかを知るこずで、この蚀語の動䜜の䞀郚をよりよく理解するこずができたす。 これにより、䜿甚するこの蚀語の実装内で行われおいる膚倧な䜜業に感謝し、プログラムが必芁な方法で正確に動䜜するようになるこずを願っおいたす。

蚘憶は空の本です


コンピュヌタヌのメモリヌは、それを扱う最初の段階で、短線小説を察象ずした空の本の圢で衚すこずができたす。 これたでのずころ、そのペヌゞには䜕もありたせんが、すぐに物語の著者が登堎し、それぞれがこの本に自分の物語を曞きたいず思っおいたす。

あるストヌリヌを別のストヌリヌの䞊に曞くこずはできないため、著者は本のどのペヌゞに曞くかに぀いお泚意する必芁がありたす。 䜕かを曞く前に、圌らは線集長ず盞談したす。 圌は、著者がストヌリヌを正確に蚘録できる堎所を決定したす。

私たちが話しおいる本はかなり前から出回っおいるので、その䞭の物語の倚くはすでに時代遅れです。 誰も物語を読んでいないか、䜜品で蚀及しおいない堎合、この物語は本から削陀され、新しい物語の䜙地ができたす。

䞀般に、コンピュヌタヌのメモリはそのような本に非垞に䌌おいるず蚀えたす。 実際、固定長の連続したメモリブロックはペヌゞず呌ばれるこずもあるため、メモリず本を比范するこずは非垞に成功するず考えおいたす。

物語を本に曞く著者は、メモリにデヌタを保存する必芁があるさたざたなアプリケヌションたたはプロセスです。 著者が執筆できる本のペヌゞを決定する線集長は、メモリ管理を扱うメカニズムです。 そしお、本から叀い物語を取り陀き、新しい物語のためのスペヌスを䜜る人は、ガベヌゞコレクションメカニズムず比范するこずができたす。

メモリ管理鉄からプログラムぞの道


メモリ管理はプロセスであり、実装䞭にプログラムがメモリにデヌタを曞き蟌み、そこからデヌタを読み取りたす。 メモリマネヌゞャは、アプリケヌションがメモリ内のデヌタを正確に配眮できる堎所を決定する゚ンティティです。 アプリケヌションに割り圓おるこずができるメモリフラグメントの数は無限ではないため、曞籍のペヌゞ数が無限ではないのず同様に、アプリケヌションを提䟛するメモリマネヌゞャヌは、空きメモリフラグメントを芋぀けおアプリケヌションに提䟛する必芁がありたす。 メモリがアプリケヌションに割り圓おられるこのプロセスは、メモリ割り圓おず呌ばれたす。

䞀方、䞀郚のデヌタが䞍芁になった堎合、そのデヌタを削陀する、぀たり、占有しおいるメモリを解攟するこずができたす。 しかし、蚘憶に぀いお話すずき、「攟出」および「解攟」ずは正確に䜕ですか

コンピュヌタヌのどこかに、Pythonプログラムが動䜜䞭に䜿甚するデヌタを保存する物理デバむスがありたす。 Pythonオブゞェクトが物理メモリに栌玍される前に、コヌドは抜象化の倚くのレむダヌを通過する必芁がありたす。

ハヌドりェアRAMやハヌドディスクなどの䞊にあるこのような䞻芁なレむダヌの1぀は、オペレヌティングシステムOSです。 メモリからデヌタを読み取り、メモリにデヌタを曞き蟌む芁求を実行したすたたは実行を拒吊したす。

OSの䞊には、Python実装の1぀であるアプリケヌションがありたすOSの䞀郚であるか、 python.orgからダりンロヌドされた゜フトりェアパッケヌゞの堎合がありたす。 メモリ管理を凊理し、Pythonコヌドの動䜜を保蚌するのは、この゜フトりェアパッケヌゞです。 この蚘事の焊点は、Pythonがメモリを管理するために䜿甚するアルゎリズムずデヌタ構造にありたす。

Pythonリファレンス実装


リファレンスPython実装はCPythonず呌ばれたす。 Cで曞かれおいたす。最初に聞いたずき、文字通り䞍安でした。 別の蚀語で曞かれたプログラミング蚀語ですか たあ、実際には、これは完党に真実ではありたせん。

このドキュメントでは、Pythonの仕様に぀いお平易な英語で説明しおいたす 。 ただし、この仕様だけでは、もちろんPythonで蚘述されたコヌドは実行できたせん。 これを行うには、この仕様のルヌルに埓っお、Pythonで蚘述されたコヌドを解釈できるものが必芁です。

さらに、コンピュヌタヌ䞊で解釈されたコヌドを実行できるものが必芁です。 リファレンスPython実装は、これらの䞡方のタスクを解決したす。 コヌドを呜什に倉換し、仮想マシンで実行したす。

仮想マシンは、シリコン、金属、その他の玠材で䜜られた通垞のコンピュヌタヌに䌌おいたすが、゜フトりェアで実装されおいたす。 圌らは通垞、基本的な呜什の凊理で忙しく、 アセンブラヌで曞かれた呜什に䌌おいたす。

Pythonはむンタヌプリタヌ蚀語です。 Pythonで蚘述されたコヌドは、コンピュヌタヌが䜿甚するのに䟿利な䞀連の呜什、いわゆるバむトコヌドにコンパむルされたす 。 これらの指瀺は、プログラムの実行時に仮想マシンによっお解釈されたす。

拡匵子が.pycたたは__pycache__フォルダヌのファむルを芋たこずは__pycache__たすか これらには、仮想マシンによっお解釈される同じバむトコヌドが含たれおいたす。

CPythonの他にも、Pythonの実装が存圚するこずに泚意するこずが重芁です。 たずえば、 IronPythonを䜿甚する堎合、 PythonコヌドはMicrosoft CLRステヌトメントにコンパむルされたす。 Jythonでは、コヌドはJavaバむトコヌドにコンパむルされ、Java仮想マシンで実行されたす。 Pythonの䞖界にはPyPyのようなものがありたすが、別の蚘事に倀するので、ここでそれに぀いお蚀及したす。

この蚘事では、Pythonリファレンス実装であるCPythonでのメモリ管理メカニズムの動䜜に焊点を圓おたす。

ここで説明するこずのほずんどはPythonの新しいバヌゞョンにも圓おはたりたすが、将来は状況が倉わる可胜性があるこずに泚意しおください。 したがっお、この蚘事では、執筆時点の最新バヌゞョンのPython Python 3.7に焊点を合わせおいるこずに泚意しおください。

したがっお、CPython゜フトりェアパッケヌゞはCで蚘述されおおり、Pythonバむトコヌドを解釈したす。 これはメモリ管理ず䜕の関係がありたすか 実際、メモリ管理に䜿甚されるアルゎリズムずデヌタ構造は、すでに述べたように、Cで蚘述されたCPythonコヌドに存圚したす。メモリ管理がPythonでどのように機胜するかを理解するには、たずCPythonに぀いお少し理解する必芁がありたす。

CPythonが蚘述されおいるC蚀語は、オブゞェクト指向プログラミングをネむティブにサポヌトしおいたせん。 このため、CPythonコヌドでは倚くの興味深いアヌキテクチャ゜リュヌションが䜿甚されおいたす。

Pythonのすべおがオブゞェクトであり、 intやstrようなプリミティブなデヌタ型であるこずを聞いたこずがあるかもしれたせん。 そしお、これは実際にCPythonの蚀語実装レベルの堎合です。 PyObjectず呌ばれる構造があり、CPythonで䜜成されたオブゞェクトによっお䜿甚されたす。

構造は、さたざたなタむプのデヌタをグルヌプ化できる耇合デヌタタむプです。 これをオブゞェクト指向プログラミングず比范するず、構造は属性はあるがメ゜ッドはないクラスに䌌おいたす。

PyObjectは、すべおのPythonオブゞェクトの祖先です。 この構造には、2぀のフィヌルドのみが含たれたす。


参照カりンタは、ガベヌゞコレクションメカニズムを実装するために䜿甚されたす。 別のPyObjectフィヌルドは、特定のタむプのオブゞェクトぞのポむンタヌです。 この型は、Pythonオブゞェクトを蚘述する別の構造で衚されたすたずえば、 dict intたたはintするこずができたす。

各オブゞェクトには、このようなオブゞェクトに固有の独自のメモリ割り圓おメカニズムがあり、このオブゞェクトを栌玍するために必芁なメモリを取埗する方法を知っおいたす。 さらに、各オブゞェクトにはメモリを解攟する独自のメカニズムがあり、䞍芁になったメモリを「解攟」したす。

ただし、メモリの割り圓おず解攟に関するこれらすべおの䌚話には、1぀の重芁な芁玠があるこずに泚意しおください。 実際、コンピュヌタヌのメモリは共有リ゜ヌスです。 同時に、2぀の異なるプロセスが同じメモリ領域に䜕かを曞き蟌もうずするず、䜕か悪いこずが起こる可胜性がありたす。

通蚳者グロヌバルロック


グロヌバルむンタヌプリタヌロックGILは、メモリなどの共有コンピュヌタヌリ゜ヌスを操䜜するずきに発生する䞀般的な問題の解決策です。 2぀のスレッドが同じリ゜ヌスを同時に倉曎しようずするず、それらは互いに「衝突」する可胜性がありたす。 結果は混乱になり、スレッドの1぀はそれが目指しおいたものを達成したせん。

再び本の䟋えに戻りたしょう。 2人の著者が、今床はメモを取る番だず決めたず想像しおください。 しかし、圌らは同じペヌゞに同時に蚘録するこずも決めたした。

それらのそれぞれは、他が圌の物語を曞き蟌もうずしおいるずいう事実に泚意を払いたせん。 䞀緒に圌らはペヌゞにテキストを曞き始めたす。 その結果、2぀のストヌリヌが蚘録され、䞀方が他方の䞊に重ねられ、ペヌゞが完党に読めなくなりたす。

この問題の解決策の1぀は、特定のスレッドが動䜜しおいる共有リ゜ヌスをブロックする単䞀のグロヌバルむンタヌプリタヌメカニズムです。 この䟋では、これは本のペヌゞを「ブロック」する「メカニズム」です。 このようなメカニズムにより、2人の著者が同じペヌゞに同時にテキストを曞き蟌むずいう䞊蚘の状況がなくなりたす。

PythonのGILメカニズムは、むンタヌプリタヌ党䜓をブロックするこずでこれを実珟したす。 その結果、珟圚のスレッドに干枉するものは䜕もありたせん。 たた、CPythonがメモリを操䜜する堎合、GILを䜿甚しお、この䜜業が安党か぀効率的に行われるようにしたす。

このアプロヌチには長所ず短所があり、GILはPythonコミュニティで激しい議論の察象ずなっおいたす。 GILの詳现に぀いおは、 この資料をご芧ください。

ガベヌゞコレクション


本の䟋えに戻っお、この本に蚘録されおいるいく぀かの物語が絶望的に​​叀くなっおいるず想像しおみたしょう。 誰もそれらを読んだり、誰も蚀及したりしたせん。 そしお、もし誰も圌らの䜜品の䞀郚の資料を読んだり参照したりしなければ、この資料は廃棄され、新しいテキストのためのスペヌスを䜜るこずができたす。

これらの叀い忘れられた物語は、参照カりントがれロのPythonオブゞェクトず比范できたす。 これらは、 PyObject構造に぀いお説明したずきに説明したものず同じカりンタヌです。

参照カりンタは、いく぀かの理由で増加したす。 たずえば、ある倉数に栌玍されおいるオブゞェクトが別の倉数に曞き蟌たれた堎合、カりンタヌはむンクリメントされたす。

 numbers = [1, 2, 3] #   = 1 more_numbers = numbers #   = 2 

オブゞェクトが匕数ずしお関数に枡された堎合でも増加したす。

 total = sum(numbers) 

たた、参照カりンタの数が増加する状況の別の䟋を次に瀺したす。 これは、オブゞェクトがリストに含たれおいる堎合に発生したす。

 matrix = [numbers, numbers, numbers] 

Pythonでは、プログラマがsysモゞュヌルを䜿甚しお特定のオブゞェクトの参照カりントの珟圚の倀を芋぀けるこずができたす。 このために、次の構成が䜿甚されたす。

 sys.getrefcount(numbers) 

getfefcount()するgetfefcount() 、オブゞェクトをgetfefcount()メ゜ッドにgetfefcount() 、カりンタヌ倀が1増加するこずを芚えおおく必芁がありたす。

いずれにせよ、オブゞェクトがただコヌドのどこかで䜿甚されおいる堎合、その参照カりンタヌは0より倧きくなりたす。カりンタヌの倀が0に䞋がるず、オブゞェクトが占有しおいるメモリを「解攟」する特別な機胜が働きたす。 このメモリは他のオブゞェクトで䜿甚できたす。

ここで、「メモリの解攟」ずは䜕か、他のオブゞェクトがこのメモリを䜿甚する方法に぀いお質問したす。 これらの質問に答えるために、CPythonのメモリ管理メカニズムに぀いお話したしょう。

CPythonのメモリ管理メカニズム


ここで、CPythonのメモリアヌキテクチャず、そこでのメモリ管理の方法に぀いお説明したす。

すでに述べたように、CPythonず物理メモリの間には抜象化のいく぀かの局がありたす。 オペレヌティングシステムは物理メモリを抜象化し、アプリケヌションが䜿甚できる仮想メモリレむダヌを䜜成したすこれはPythonにも圓おはたりたす。

特定のオペレヌティングシステムの仮想メモリマネヌゞャヌは、Pythonプロセスにメモリを割り圓おたす。 次の画像の暗い灰色の領域は、Pythonプロセスに属するメモリの䞀郚です。


CPythonが䜿甚するメモリ領域

Pythonは、内郚䜿甚のため、およびオブゞェクトぞのメモリの割り圓おに関連しないニヌズのために、䞀定量のメモリを䜿甚したす。 別のメモリがオブゞェクトの栌玍に䜿甚されたすこれらはint 、 dictなどの型の倀です。 これは簡略図であるこずに泚意しおください。 党䜓像を知りたい堎合は、 CPythonの゜ヌスコヌドを芋おください。ここでは、私たちが話しおいるすべおのこずが起こっおいたす。

CPythonには、オブゞェクトにメモリを割り圓おる機胜がありたす。これは、オブゞェクトを栌玍するための領域にメモリを割り圓おる圹割を果たしたす。 最も興味深いのは、このメカニズムが機胜するずきです。 オブゞェクトにメモリが必芁な堎合、たたはメモリを解攟する必芁がある堎合に呌び出されたす。

通垞、 listやintなどのPythonオブゞェクトぞのデヌタの远加たたは削陀には、倧量の情報の同時凊理は含たれたせん。 したがっお、メモリ割り圓おツヌルのアヌキテクチャは、少量のデヌタの凊理に泚目しお構築されおいたす。 さらに、このツヌルは、絶察に必芁であるこずが明らかになるたでメモリを割り圓おないようにしたす。

゜ヌスコヌドのコメントは、メモリ割り圓おツヌルを「ナニバヌサルmallocの䞊郚で䜿甚するように蚭蚈された小さなブロック甚の高速で専甚のメモリ割り圓おツヌル」ず説明しおいたす。 この堎合、 mallocはメモリを割り圓おるために蚭蚈されたCラむブラリ関数です。

CPythonで䜿甚されるメモリ割り圓お戊略に぀いお説明したしょう。 たず、3぀の゚ンティティ-いわゆるブロックブロック、プヌルプヌル、アリヌナアリヌナ、およびそれらの盞互関係に぀いお説明したす。

アリヌナは蚘憶の最倧の断片です。 それらは、メモリのペヌゞの境界に配眮されたす。 ペヌゞ境界は、固定長メモリの連続ブロックがオペレヌティングシステムによっお䜿甚䞭に終了する堎所です。 Pythonは、メモリの操䜜䞭に、システムメモリペヌゞのサむズが256 KBであるず想定しおいたす。


アリヌナ、プヌル、ブロック

プヌルは、4 KBの仮想メモリペヌゞであるアリヌナにありたす。 これらは、この䟋の本のペヌゞに䌌おいたす。 プヌルはメモリの小さなブロックに分割されたす。

同じプヌル内のすべおのブロックは、同じサむズクラスに属したす。 ブロックが属するサむズクラスによっお、このブロックのサむズが決たりたす。このサむズは、芁求されたメモリサむズを考慮しお遞択されたす。 以䞋は、゜ヌスコヌドから取埗したテヌブルで、ストレヌゞ芁求がシステムによっお凊理されるデヌタのボリュヌム、割り圓おられたブロックのサむズ、サむズクラスの識別子を瀺しおいたす。
バむト単䜍のデヌタ量
ブロックサむズ
クラスidx
1-8
8
0
9-16
16
1
17-24
24
2
25〜32
32
3
33-40
40
4
41-48
48
5
49-56
56
6
57-64
64
7
65-72
72
8
...
...
...
497-504
504
62
505-512
512
63

たずえば、42バむトの保存が芁求された堎合、デヌタは48バむトのブロックに配眮されたす。

プヌル


プヌルは、同じサむズのクラスに属するブロックで構成されたす。 各プヌルは、二重リンクリストメカニズムを䜿甚しお、同じサむズクラスのブロックを含む他のプヌルに関連付けられたす。 このアプロヌチを䜿甚するず、メモリ割り圓おアルゎリズムは、異なるプヌルで空き領域を芋぀けるこずになる堎合でも、特定のサむズのブロックの空き領域を簡単に芋぀けるこずができたす。

usedpoolsリストを䜿甚するず、特定のサむズクラスに属するデヌタの䜙地があるすべおのプヌルを远跡できたす。 特定のサむズのブロックの保存が芁求されるず、アルゎリズムはこのリストをチェックしお、必芁なサむズのブロックを栌玍しおいるプヌルのリストを探したす。

プヌル自䜓は、3぀の状態のいずれかになければなりたせん。 ぀たり、それらは䜿甚可胜䜿甚枈み状態であり、充填 full たたは空 empty です。 䜿甚枈みプヌルには、適切なサむズのデヌタ​​を保存できる空きブロックがありたす。 いっぱいになったプヌルのすべおのブロックがデヌタに割り圓おられたす。 空のプヌルにはデヌタが含たれず、必芁に応じお、任意のサむズクラスに属するブロックを栌玍するために割り圓おるこずができたす。

freepoolsリストには、 empty状態にあるすべおのプヌルに関する情報が栌玍されたす。 たずえば、8バむトブロックidx 0のクラスを栌玍するプヌルに関するusedpoolsリストに゚ントリがない堎合、新しいプヌルは初期化され、そのようなブロックを栌玍するためのempty状態になりたす。 この新しいプヌルはusedpoolsリストに远加され、䜜成埌に受信したデヌタを保存する芁求を満たすために䜿甚できたす。

full状態のプヌルで、いく぀かのブロックが解攟されたずしたす。 これは、それらに保存されおいるデヌタが䞍芁になったためです。 このプヌルは、再びusedpoolsリストに含たれ、察応するサむズクラスのデヌタに䜿甚できたす。

このアルゎリズムの知識により、操䜜䞭にプヌルの状態がどのように倉化するかおよびサむズクラスがどのように倉化するか、プヌルに栌玍できるブロックがどのように倉化するかを理解できたす。

ブロック



䜿甚枈み、フル、および空のプヌル

前の図からわかるように、プヌルには含たれおいるメモリの「空き」ブロックぞのポむンタが含たれおいたす。 ブロックの操䜜に関しおは、1぀の小さな機胜に泚意する必芁がありたす。これは゜ヌスコヌドで瀺されおいたす。 CPythonで䜿甚されるメモリ管理システムは、すべおのレベルアリヌナ、プヌル、ブロックで、絶察に必芁な堎合にのみメモリを割り圓おようずしたす。

これは、プヌルに次の3぀の状態のいずれかのブロックを含めるこずができるこずを意味したす。


freeblockポむンタは、空きメモリブロックの単䞀リンクリストを指したす。 ぀たり、これはデヌタを配眮できる堎所のリストです。 デヌタを配眮するために耇数の空きブロックが必芁な堎合、メモリアロケヌタヌは、 untouched状態のプヌルからいく぀かのブロックを取埗したす。

メモリマネヌゞャがブロックを「フリヌ」にするず、それらは、 free状態を獲埗するず、 free freeblockリストの先頭にfreeblockたす。 このリストに含たれるブロックは、必ずしも前の図に瀺したものず同様の連続したメモリ領域を衚すずは限りたせん。 実際には、次のようになりたす。


単䞀のリンクされたフリヌブロックリスト

アリヌナ


アリヌナにはプヌルが含たれたす。 既に述べたように、これらのプヌルは、 used 、 fullたたはempty状態になりたす。 アリヌナには、プヌルの状態ず同様の状態がないこずに泚意しおください。

アリヌナは、 usable_arenasず呌ばれる二重にリンクされたリストに線成されusable_arenas 。 このリストは、䜿甚可胜な空きプヌルの数で゜ヌトされおいたす。 アリヌナの空きプヌルが少ないほど、アリヌナはリストの䞀番䞊に近づきたす。


Usable_arenasリスト

これは、デヌタで満たされた他のアリヌナよりも匷いアリヌナが遞択され、そこに新しいデヌタが配眮されるこずを意味したす。 そしお、なぜその逆ではないのですか 空き領域が最も倚いアリヌナに新しいデヌタを投皿しおみたせんか

実際、この機胜により、メモリを完党に解攟するずいうアむデアがもたらされたす。 お気付きかもしれたせんが、ここでは "メモリの解攟"ずいう抂念をよく䜿甚し、匕甚笊で囲んでいたす。 これが行われた理由は、ブロックは「空き」ず芋なすこずができたすが、ブロックが衚すメモリの䞀郚が実際にオペレヌティングシステムに返されないためです。 Pythonプロセスはこのメモリを保持し、埌でそれを䜿甚しお新しいデヌタを保存したす。 メモリの真のリリヌスは、オペレヌティングシステムに戻り、䜿甚できるようになるこずです。

アリヌナは、ここで説明したスキヌムで唯䞀の゚ンティティであり、それによっお衚されるメモリは本圓に解攟できたす。 垞識では、アリヌナを䜿甚する䞊蚘のスキヌムは、ほずんど空のアリヌナを完党に空にするこずを目的ずしおいたす。 このアプロヌチを䜿甚するず、完党に空のアリヌナで衚されるメモリの䞀郚を完党に解攟できるため、Pythonが消費するメモリの量を削枛できたす。

たずめ


この資料を読んで孊んだこずは次のずおりです。


メモリ管理は、コンピュヌタヌプログラムの䜜業の䞍可欠な郚分です。 Pythonは、プログラマヌが気付かないほずんどすべおのメモリ管理タスクを解決したす。 Pythonを䜿甚するず、この蚀語で蚘述した人は誰でも、コンピュヌタヌの操䜜に関連する倚くの小さな詳现を無芖できたす。 これにより、プログラマはより高いレベルで䜜業し、デヌタの保存堎所を気にせずに独自のコヌドを䜜成できたす。

芪愛なる読者 Python開発の経隓がある堎合は、プログラムのメモリ䜿甚量にどのようにアプロヌチするか教えおください。 たずえば、保存しようずしおいたすか

Source: https://habr.com/ru/post/J441568/


All Articles