Introduction

メモリ管理とは、すべての異なるプロセスがスムーズに動作し、異なるシステムリソースに最適にアクセスできるように、メモリを効率的に割り当て、割り当て解除し、調整するプロセスのことです。

Python では、メモリ マネージャーが定期的に実行されてメモリをクリーンアップ、割り当て、管理することにより、これらの種類のタスクに責任を負います。 C や Java、その他のプログラミング言語とは異なり、Python は参照カウントを使用してオブジェクトを管理します。 つまり、メモリマネージャは、プログラム内の各オブジェクトへの参照数を記録しているのです。 オブジェクトの参照カウントがゼロになると、つまり、オブジェクトがもう使用されなくなると、ガベージコレクタ(メモリマネージャの一部)が自動的にその特定のオブジェクトからメモリを解放します。

メモリの割り当てと割り当て解除のプロセスは完全に自動化されているので、ユーザーはメモリ管理について心配する必要がありません。

Python ガーベッジコレクション

先に説明したように、Python はメモリ領域を解放するために、プログラム内で参照されなくなったオブジェクトを削除します。 Python が、もはや使用されないメモリ ブロックを解放するこのプロセスは、ガーベッジ コレクションと呼ばれます。 Pythonのガーベジコレクタ(GC)はプログラムの実行中に実行され、参照カウントがゼロになったときにトリガされます。 参照カウントは、オブジェクトに新しい名前が割り当てられたり、タプルや辞書のようなコンテナに配置されたりすると増加します。 同様に、オブジェクトへの参照が再割り当てされたとき、オブジェクトの参照が範囲外になったとき、またはオブジェクトが削除されたときに参照カウントが減少する。

メモリはヒープであり、プログラムで使用するオブジェクトや他のデータ構造を含む。 このヒープ空間の割り当てと解除は、API 関数の使用により Python Memory manager によって制御されます。

メモリ内の Python オブジェクト

Pythonの各変数はオブジェクトとして機能します。 オブジェクトは単純なもの (数値、文字列などを含む) とコンテナ (辞書、リスト、またはユーザー定義のクラス) のいずれかになります。 さらに、Python は動的型付け言語であるため、プログラムで使用する前に変数やその型を宣言する必要がありません。

たとえば、

上記のプログラムの最初の 2 行を見ると、オブジェクト x が知られています。 オブジェクト x を削除して使おうとすると、変数 x が定義されていないというエラーが発生します。

Python のガベージコレクションは完全に自動化されていて、C 言語などと違ってプログラマが心配する必要はないことがわかります。 ライフサイクルの開始点にある新しいオブジェクトは、ガベージコレクタの第1世代です。 オブジェクトがガベージコレクションを生き残るにつれて、次の世代に移動されます。 ガベージコレクタの3世代はそれぞれ閾値を持っている。 具体的には、割り当て数から削除数を引いた閾値を超えると、その世代はガベージコレクションを実行します。

初期の世代も、上位の世代よりも頻繁にガベージコレクションが実行されます。 これは、新しいオブジェクトの方が古いオブジェクトよりも廃棄される可能性が高いためです。

モジュールには、閾値を変更する、ガベージコレクション処理を手動で起動する、ガベージコレクション処理を無効にする、などの関数が含まれています。 ガベージコレクタの異なる世代のしきい値をget_threshold()メソッドで確認できます。

import gcprint(gc.get_threshold())

サンプル出力:

(700, 10, 10)

ご覧のように、ここでは、第1世代は700、他の2世代はそれぞれ10というしきい値になっています。

gc モジュールの set_threshold() メソッドを使用して、ガベージ コレクション処理をトリガーする閾値を変更できます:

gc.set_threshold(900, 15, 15)

上記の例では、3 つの世代すべてで閾値を高くしています。 閾値を大きくすることで、ガベージコレクタの実行頻度を減らすことができます。 通常、開発者としてPythonのガベージコレクションについて深く考える必要はありませんが、ターゲットシステムに合わせてPythonランタイムを最適化する際には有効かもしれません。 重要な利点の 1 つは、Python のガベージコレクション機構が開発者のために多くの低レベルの詳細を自動的に処理することです。

なぜ手動ガベージコレクションを実行するのか

Python インタープリターは、プログラムで使用するオブジェクトへの参照を追跡していることを私たちは知っています。 Python の以前のバージョン (バージョン 1.6 まで) では、Python インタープリターはメモリを処理するために参照カウント機構のみを使用していました。 参照カウントがゼロになると、Pythonインタープリタは自動的にメモリを解放します。 この古典的な参照カウントの仕組みは非常に効果的ですが、プログラムが参照サイクルを持つ場合に機能しないことを除けば、非常に効果的です。

>>> def create_cycle():... list = ... list.append(list)... return list... >>> create_cycle()]

上のコードは参照サイクルを作成し、オブジェクト list は自分自身を参照します。 したがって、オブジェクト list のメモリは、関数が戻ったときに自動的に解放されません。 参照サイクルの問題は参照カウントでは解決できない。

これを行うには、gc モジュールの gc.collect() 関数を使用します。

import gcn = gc.collect()print("Number of unreachable objects collected by GC:", n)

gc.collect() は収集し割り当てを解除したオブジェクトの数を返します。

手動ガベージコレクションを実行するには、タイムベースまたはイベントベースのガベージコレクションの2つの方法があります。

タイムベースのガベージコレクションは非常にシンプルで、一定の時間間隔の後に gc.collect() 関数が呼ばれます。

イベント ベースのガベージ コレクションは、イベントが発生した後 (すなわち、アプリケーションが終了したとき、またはアプリケーションが特定の時間アイドル状態のとき)、gc.collect() 関数を呼び出します。

いくつかの参照サイクルを作成して、手動ガベージ コレクション作業を理解しましょう。 リストオブジェクトの最初の要素は自分自身を参照している。 リストオブジェクトの参照カウントは、プログラム中で削除されたり、スコープ外になったりしても、常に0より大きい。 したがって、listオブジェクトは循環参照によりガベージコレクションされない。

上記のコードでは、参照カウントが少なくとも1であり、0になることはないので、gc.collect()を呼び出してオブジェクトを強制的にガベージコレクションしています。 しかし、強制的にガベージコレクションを頻繁に行わないように注意してください。 なぜなら、メモリを解放した後でも、GCはオブジェクトがガベージコレクションされる資格があるかどうかを評価するのに時間がかかり、プロセッサの時間とリソースを消費してしまうからです。 また、アプリが完全に起動した後にのみ、ガベージコレクターを手動で管理することを忘れないでください。

Conclusion

この記事では、参照カウントとガベージコレクション戦略を使用して、Python のメモリ管理がどのように自動的に処理されるかを議論しました。 ガベージコレクションなしでは、Python で成功するメモリ管理機構を実装することは不可能です。 また、プログラマは割り当てられたメモリを削除することを心配する必要はありません、なぜならそれはPythonのメモリマネージャによって世話されるからです。 これは、より少ないメモリリークとより良い性能につながります。

コメントを残す

メールアドレスが公開されることはありません。