Pythonのガベージコレクション:知っておくべきことすべて
Jan 17, 2025
# python
Emily Parker
Product Engineer · Leapcell

I. ガベージコレクションの概要
- コンピュータサイエンスにおいて、ガベージコレクション(GCと略される)は自動メモリ管理機構です。プログラムによって占有されている特定のメモリー空間がアクセスされなくなると、プログラムはガベージコレクションアルゴリズムを通じてそれをオペレーティングシステムに返却します。
- ガベージコレクターはプログラマーの負担を軽減し、プログラムのエラーを最小限に抑えることができます。これはLISP言語から生まれました。
- 現在、Smalltalk、Java、C#、Go、Dなどの多くの言語がガベージコレクターをサポートしています。
- 最新のプログラミング言語の自動メモリ管理機構として、GCは主に次の2つのタスクを実行します。
- メモリ内の不要なガベージ・リソースを識別する。
- これらのガベージをクリアし、他のオブジェクトが利用できるようにメモリを解放する。
- プログラマーをリソース管理の重い負担から解放し、ビジネスロジックに集中できるようにします。ただし、プログラマーはGCを理解する必要があり、これにより、より堅牢なコードを作成するのに役立ちます。
II. 一般的なガベージコレクションアルゴリズム
- 参照カウント:
- 各オブジェクトの参照カウントを維持します。このオブジェクトを参照するオブジェクトが破棄されると、参照カウントは1つ減ります。オブジェクトの参照カウントがゼロになると、オブジェクトはリサイクルされます。
- 代表的な言語:Python、PHP、Swift
- 利点:オブジェクトのリサイクルが速く、メモリが枯渇した場合や特定のしきい値に達した場合にのみリサイクルするわけではありません。
- 欠点:循環参照を効果的に処理できず、リアルタイムで参照カウントを維持するとオーバーヘッドが発生します。
- マーク・アンド・スイープ:
- ルート変数から始まるすべての参照オブジェクトをトラバースし、参照オブジェクトをマークし、マークされていないオブジェクトをリサイクルします。
- 代表的な言語:Golang(トライカラーマーキング方式)、Python(補助)。
- 利点:参照カウントの欠点を克服します。
- 欠点:STW(プログラムの実行を一時的に停止する)が必要です。
- 世代別コレクション:
- オブジェクトの寿命に応じて異なる世代空間を分割します。寿命の長いオブジェクトは古い世代に配置され、寿命の短いオブジェクトは新しい世代に配置されます。世代が異なれば、リサイクルアルゴリズムと頻度も異なります。
- 代表的な言語:Java、Python(補助)。
- 利点:優れたリサイクル性能。
- 欠点:アルゴリズムが複雑です。
III. Pythonのガベージコレクション機構
- 公式ドキュメントからの説明:
- Pythonのメモリ管理の詳細は実装によって異なります。
- CPythonは参照カウントを使用してアクセス不能なオブジェクトを検出し、別のメカニズムを使用して参照サイクルを収集します。周期的にサイクル検出アルゴリズムを実行してアクセス不能なサイクルを見つけ、関連するオブジェクトを削除します。
- gcモジュールは、ごみ収集の実行、デバッグ統計の取得、およびコレクターパラメーターの最適化のための機能を提供します。
- その他の実装(JythonやPyPyなど)は、完全なガベージコレクターなど、異なるメカニズムに依存する場合があります。Pythonコードが参照カウントによって実装された動作に依存している場合、移植性の問題が発生する可能性があります。
- 参照カウント:
- Pythonが採用しているデフォルトのガベージコレクションメカニズムは、参照カウント方式です。これは1960年にGeorge E. Collinsによって最初に提案され、今日でも多くのプログラミング言語で使用されています。
- 原理:各オブジェクトは、オブジェクトが現在参照されている回数を記録するob_refフィールドを保持します。新しい参照がオブジェクトを指すと、ob_refは1つずつインクリメントされます。参照が無効になると、ob_refは1つ減ります。参照カウントがゼロになると、オブジェクトは直ちにリサイクルされ、占有されているメモリ空間が解放されます。
- 欠点:参照カウントを維持するために追加のスペースが必要となり、オブジェクトの「循環参照」を解決できません。例:
a = {} # オブジェクトAの参照カウントは1です b = {} # オブジェクトBの参照カウントは1です a['b'] = b # Bの参照カウントは1ずつインクリメントされます b['a'] = a # Aの参照カウントは1ずつインクリメントされます del a # Aの参照は1つずつデクリメントされ、オブジェクトAの最終的な参照は1です del b # Bの参照は1つずつデクリメントされ、オブジェクトBの最終的な参照は1です
- 上記の例では、delステートメントが実行された後、オブジェクトAとBは循環参照を形成します。外部参照はありませんが、参照カウントはゼロではないため、リサイクルされず、メモリリークが発生する可能性があります。
- マーク・アンド・スイープ:
- これは、トレーシングGCテクノロジーに基づいて実装されたガベージコレクションアルゴリズムであり、次の2つのフェーズで構成されています。
- マーキングフェーズ:「アクティブなオブジェクト」をすべてマークします。
- スイープフェーズ:マークされていない「非アクティブなオブジェクト」をリサイクルします。
- ルートオブジェクト(グローバル変数、コールスタック、レジスタなど)から開始して、有向エッジに沿ってオブジェクトをトラバースします。到達可能なオブジェクトはアクティブなオブジェクトとしてマークされ、到達不能なオブジェクトは非アクティブなオブジェクトとしてマークされてからクリアされます。
- マーク・アンド・スイープアルゴリズムは、Pythonの補助的なガベージコレクションテクノロジーとして、主にコンテナオブジェクト(リスト、dict、タプル、インスタンスなど)を処理します。これは、文字列オブジェクトと数字オブジェクトは循環参照の問題を引き起こさないためです。
- Pythonは、二重リンクリストを使用してこれらのコンテナオブジェクトを編成します。
- 欠点:非アクティブなオブジェクトをクリアする前に、ヒープメモリ全体を順番にスキャンする必要があります。アクティブなオブジェクトがごく一部しか残っていなくても、すべてのオブジェクトをスキャンする必要があります。
- これは、トレーシングGCテクノロジーに基づいて実装されたガベージコレクションアルゴリズムであり、次の2つのフェーズで構成されています。
- 世代別リサイクル:
- これは、スペースと時間を交換する操作モードです。メモリはオブジェクトの生存時間に応じて異なるセットに分割され、各セットは世代と見なされます。Pythonは、若い世代(世代0)、中間の世代(世代1)、および古い世代(世代2)の3つの世代に分割され、3つのリンクリストに対応します。オブジェクトの生存時間が長くなるにつれて、ガベージコレクションの頻度が減少します。
- 新しく作成されたオブジェクトは、若い世代に割り当てられます。若い世代のリンクリストの合計数が上限に達すると、ガベージコレクションメカニズムがトリガーされます。リサイクル可能なオブジェクトはリサイクルされ、リサイクルできないオブジェクトは中間の世代に移動されます。古い世代のオブジェクトは最も長く存続します。
- 世代別リサイクルは、マーク・アンド・スイープテクノロジーに基づいており、Pythonの補助的なガベージコレクションテクノロジーとしても機能し、コンテナオブジェクトを処理します。
IV. メモリリーク
- 毎日のPythonの使用では、メモリリークは比較的まれです。
- CPythonが終了時にすべてのメモリを解放しない状況:
- グローバル名前空間またはPythonモジュールから参照されるオブジェクトは常に解放されるとは限りません。循環参照がある場合に発生する可能性があります。Cライブラリによって割り当てられた一部のメモリも解放されない可能性があります。
- Pythonは、終了時にメモリをクリーンアップし、各オブジェクトを破棄しようとします。
- Pythonにリリース時に特定の内容を強制的に削除させたい場合は、atexitモジュールを使用して関数を実行できます。
- コード例:
# 一部のPython実装では、次のコード(CPythonでは正常に動作します)でファイル記述子がなくなる可能性があります for file in very_long_list_of_files: f = open(file) c = f.read(1)
- 改善されたソリューション:
# ファイルを明示的に閉じるか、withステートメントを使用する必要があります。これは、メモリ管理スキームに関係なく効果的です for file in very_long_list_of_files: with open(file) as f: c = f.read(1)
Leapcell: Pythonアプリをホスティングするための最適なサーバーレスプラットフォーム
最後に、Pythonサービスをデプロイするのに最適なプラットフォームをご紹介します。Leapcell
1. 多言語サポート
- JavaScript、Python、Go、またはRustを使用して開発します。
2. 無制限のプロジェクトを無料でデプロイ
- 実際の使用量に対してのみ料金が発生します。リクエストがない場合は料金は発生しません。
3. 比類のないコスト効率
- アイドル料金なしの従量課金制。
- 例:25ドルで、平均応答時間が60ミリ秒の694万件のリクエストをサポートできます。
4. 合理化された開発者エクスペリエンス
- セットアップが簡単な直感的なUI。
- 完全に自動化されたCI/CDパイプラインとGitOps統合。
- 実用的な洞察のためのリアルタイムのメトリックとロギング。
5. 容易なスケーラビリティと高いパフォーマンス
- 高い同時実行性を容易に処理するための自動スケーリング。
- 運用上のオーバーヘッドはゼロです。構築に集中してください。
Leapcell Twitter: https://x.com/LeapcellHQ