Pythonのガベージコレクション(GC)完全解説

[![Group19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/67521647-1fb5-3099-d738-66e08c3aeb19.png)](https://leapcell.io/?lc_t=q_pyg)

### I. ガベージコレクションの概要
- コンピュータサイエンスにおいて、ガベージコレクション（略称GC）は自動メモリ管理メカニズムです。プログラムが占有している特定のメモリ空間がもはやアクセスされなくなったとき、プログラムはガベージコレクションアルゴリズムを通じてそれをオペレーティングシステムに返します。
- ガベージコレクタは、プログラマの負担を軽減し、プログラムエラーを最小限に抑えることができます。それはLISP言語に由来します。
- 現在、Smalltalk、Java、C#、Go、Dなどの多くの言語がガベージコレクタをサポートしています。
- 現代のプログラミング言語の自動メモリ管理メカニズムとして、GCは主に2つのタスクを実行します：
  - メモリ内の不要なガベージリソースを特定します。
  - これらのガベージをクリアし、メモリを解放して他のオブジェクトが利用できるようにします。
- それはプログラマをリソース管理の重荷から解放し、より多くの精力をビジネスロジックに集中させることができます。ただし、プログラマは依然としてGCを理解する必要があり、これはより堅牢なコードを書くのに役立ちます。

### II. 一般的なガベージコレクションアルゴリズム
- **参照カウント**：
  - 各オブジェクトに参照カウントを保持します。このオブジェクトを参照するオブジェクトが破棄されると、参照カウントが1減少します。オブジェクトの参照カウントが0に達すると、オブジェクトはリサイクルされます。
  - 代表的な言語：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減少します。参照カウントが0になると、オブジェクトは直ちにリサイクルされ、占有していたメモリ空間が解放されます。
  - 欠点：参照カウントを維持するために追加の空間が必要であり、オブジェクトの「循環参照」を解決できません。例えば：
```python
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
```

[![recycle-refrence.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/5bf0608b-56bb-9af5-edc9-b58ac08a26b6.jpeg)](https://leapcell.io/?lc_t=q_pyg)


  - 上記の例では、del文が実行された後、オブジェクトAとBは循環参照を形成します。外部参照はないものの、参照カウントが0ではないため、リサイクルされず、メモリリークの原因となる可能性があります。
- **マークスイープ**：
  - トレーシングGC技術に基づいて実装されるガベージコレクションアルゴリズムで、2つのフェーズから構成されます：
    - マーキングフェーズ：すべての「アクティブなオブジェクト」にマークを付けます。
    - スイープフェーズ：マークされていない「非アクティブなオブジェクト」をリサイクルします。
  - ルートオブジェクト（グローバル変数、コールスタック、レジスタなど）から始まり、有向辺に沿ってオブジェクトをトラバースします。到達可能なオブジェクトはアクティブなオブジェクトとしてマークされ、到達不能なオブジェクトは非アクティブなオブジェクトとしてマークされ、その後クリアされます。
  - マークスイープアルゴリズムは、Pythonの補助的なガベージコレクション技術として、主にコンテナオブジェクト（list、dict、tuple、インスタンスなど）を処理します。なぜなら、文字列や数値オブジェクトは循環参照の問題を引き起こさないからです。
  - Pythonは二重リンクリストを使用してこれらのコンテナオブジェクトを整理します。
  - 欠点：非アクティブなオブジェクトをクリアする前に、ヒープメモリ全体を順次スキャンする必要があります。アクティブなオブジェクトがほんの一部だけ残っている場合でも、すべてのオブジェクトをスキャンする必要があります。
- **世代別リサイクル**：
  - これは、空間を時間と交換する動作モードです。メモリは、オブジェクトの生存時間に応じて異なるセットに分割され、各セットは1世代と見なされます。Pythonは3世代に分割されています：若い世代（世代0）、中間世代（世代1）、古い世代（世代2）で、それぞれ3つのリンクリストに対応しています。オブジェクトの生存時間が長くなるにつれて、ガベージコレクションの頻度が減少します。
  - 新しく作成されたオブジェクトは若い世代に割り当てられます。若い世代のリンクリストの総数が上限に達すると、ガベージコレクションメカニズムがトリガーされます。リサイクル可能なオブジェクトはリサイクルされ、非リサイクル可能なオブジェクトは中間世代に移動され、という具合です。古い世代のオブジェクトは最も長く生存します。
  - 世代別リサイクルはマークスイープ技術に基づいており、Pythonにおけるコンテナオブジェクトを処理する補助的なガベージコレクション技術としても機能します。

[![mark-sweep.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/993beff4-0d9d-4d0f-459e-15454b5a2441.png)](https://leapcell.io/?lc_t=q_pyg)


### IV. メモリリーク
- メモリリークは、日常的なPythonの使用では比較的まれです。
- CPythonが終了時にすべてのメモリを解放しない状況：
  - グローバル名前空間やPythonモジュールから参照されるオブジェクトは必ずしも解放されず、これは循環参照がある場合に発生する可能性があります。Cライブラリによって割り当てられた一部のメモリも解放されない場合があります。
  - Pythonは終了時にメモリをクリーンアップし、各オブジェクトを破棄しようとします。
  - 解放時に特定のコンテンツを強制的に削除したい場合は、atexitモジュールを使用して関数を実行できます。
- コード例：
```python
# 一部のPython実装では、以下のコード（CPythonでは正常に動作する）がファイルディスクリプタを枯渇させる可能性があります
for file in very_long_list_of_files:
    f = open(file)
    c = f.read(1)
```
- 改善策：
```python
# 明示的にファイルをクローズするか、メモリ管理方式に関係なく有効なwith文を使用する必要があります
for file in very_long_list_of_files:
    with open(file) as f:
        c = f.read(1)
```


# [Leapcell: Pythonアプリホスティングの最適なサーバレスプラットフォーム](https://leapcell.io/?lc_t=q_pyg)

[![barndpic.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/ad932f03-a2d1-9078-6d4d-3959bb6a165e.png)](https://leapcell.io/?lc_t=q_pyg)


最後に、Pythonサービスを展開するのに最適なプラットフォームを紹介します：[Leapcell](https://leapcell.io/?lc_t=q_pyg)

#### 1. 多言語対応
- JavaScript、Python、Go、またはRustを使用して開発できます。  

#### 2. 無料で無制限のプロジェクトを展開
- 実際の使用量に応じてのみ支払います — リクエストがなければ料金はかかりません。


#### 3. 圧倒的なコスト効率
 - 使い放題でアイドル料金はありません。  
 - 例：25ドルで、平均応答時間60ミリ秒で694万回のリクエストをサポートできます。  

#### 4. シンプルな開発者体験
 - 直感的なUIで簡単にセットアップできます。  
 - 完全自動化されたCI/CDパイプラインとGitOps統合。  
 - アクション可能なインサイトを得るためのリアルタイムメトリックとロギング。  

#### 5. 簡単なスケーラビリティと高性能
 - 高い同時実行数を簡単に処理するための自動スケーリング。  
 - オペレーションのオーバーヘッドはゼロ — 構築に集中できます。  

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3967621/d8f95828-1821-5fbe-53f7-3e1576e95df5.png)](https://leapcell.io/?lc_t=q_pyg)


[ドキュメントでもっと詳しく調べる！](https://docs.leapcell.io/)

LeapcellのTwitter：[https://x.com/LeapcellHQ](https://x.com/LeapcellHQ)