プログラムは実行中に大量のオブジェクトを作り、使い終わったメモリを返す必要があります。
ガーベジコレクション(GC)は、この返却を自動で行う仕組みです。
本記事では、GCの基本と処理の流れを、初心者でもイメージできるようにステップごとに丁寧に解説します。
プログラミング初心者向け ガーベジコレクション(GC)の基本
メモリの自動回収の目的
GCの目的は、使われなくなったメモリを自動で回収し、プログラムの安定動作とメモリ不足の防止を実現することです。
人が解放忘れをするとメモリリークが起きますが、GCは参照を辿って不要な領域を見つけて解放します。
これにより、初心者でも安心してオブジェクトを生成できる基盤が用意されます。
手動メモリ管理との違い
手動管理はプログラマが明示的に解放します。
一方でGCはランタイムが自動で解放します。
自動化により安全性が上がる一方、GCが動く瞬間にわずかな停止が起きることがあります。
まずはこのトレードオフを理解すると混乱が減ります。
以下はシンプルな比較です。
| 方式 | 代表言語 | メモリ解放のやり方 | メリット | 注意点 |
|---|---|---|---|---|
| 手動管理 | C/C++ | free/deleteなどを自分で呼ぶ | 実行のタイミングを制御しやすい | 解放忘れや二重解放のバグが起きやすい |
| GCあり | Java/C#/Go/JavaScript/Pythonなど | ランタイムが不要領域を自動回収 | 安全性が高く学習コストが低い | 回収時に短い停止やオーバーヘッドがある |
初心者はまずGCありの言語から学ぶと安全に開発を進めやすいです。
仕組みの全体像
GCは「到達可能なオブジェクトは残し、到達不能なオブジェクトを回収する」仕組みです。
具体的には、実行中の関数のローカル変数やグローバル変数などを起点(ルート)に参照を辿り、生きているオブジェクトを見つけて印を付けます。
その後、印のないオブジェクトを解放し、必要に応じてメモリを並べ直します。
初心者がまず知るポイント
- 自分で解放しない代わりに「参照を残さない」ことが重要です。
- 配列やマップ、グローバル変数に入れたままにすると、回収されません。
- 使い終わったら変数を上書きする、またはnullにすることで参照を外せます。
- ファイルやネットワーク接続はGCの対象外なので、必ず明示的に閉じます。
メモリ自動回収の流れ
ステップ1 メモリ確保
プログラムが新しいオブジェクトを作ると、ヒープという領域から必要なメモリが確保されます。
確保はとても速く、通常は「ポインタを前に進めるだけ」のような軽い処理で行われます。
こうして作られたオブジェクトは、変数や配列などから参照されます。
補足
ローカル変数自体はスタックに置かれますが、そこから参照する大きなデータはヒープに置くのが一般的です。
ステップ2 参照関係の把握
GCはまず「何が生きているか」を知るために、ルートから参照を辿ります。
ルートとは、実行中の関数のローカル変数、グローバル変数、静的フィールド、スレッドのスタックなどです。
ここから矢印をたどって届くオブジェクトは「使用中」とみなされます。
イメージ
ルートを出発点とする線でつながった箱は生存、線が一切届かない箱は不要、と考えると理解しやすいです。
ステップ3 使用中を印付け
参照を辿りながら、生きているオブジェクトに印(マーク)を付けます。
この段階では「残すもの」と「捨てるもの」を区別するだけで、まだ解放はしません。
マークが付いたものは次の段階で保護されます。
補足
マーク処理はプログラムを一時停止してまとめて行うこともあれば、少しずつ分けて進める方式もあります。
ステップ4 使っていないメモリを回収
印のないオブジェクトは不要と判断され、解放されます。
これが「スイープ」です。
空いた領域は次の確保に再利用されます。
多くのシステムではこの時点で大部分の不要メモリが戻ります。
注意
不要なメモリが大量に溜まってからスイープが走ると、まとめて回収されるため一時的に処理が止まることがあります。
ステップ5 メモリを並べ直す
一部のGCは、解放後に生存オブジェクトを詰めて連続させます。
これをコンパクションと呼び、断片化を防いで次の確保を速くします。
オブジェクトが移動した場合は、参照先が正しく指すように更新します。
イメージ
本棚の空きスペースを詰めるように、バラバラな本を左に寄せる感覚です。
ステップ6 プログラム再開と次回の準備
回収と並べ直しが終わると、プログラムは続きから再開します。
最近のGCは、停止時間を短くするために「少し止める」「分割して進める」などの工夫をしています。
また、次回の回収に役立つ統計情報を更新し、無駄のないタイミングで走れるよう準備します。
補足
言語や実装によって停止時間や回収の細かい挙動は異なりますが、基本の流れは共通です。
いつGCが走るかの目安
GCは「ヒープが一定以上使われた」「短時間に大量のオブジェクトを作った」「アイドル時間ができた」などのタイミングで走ることが多いです。
実装によってはOSからのメモリ圧力でも起動します。
多くの言語で明示的にGCを要求する関数はありますが、常用は推奨されません(ランタイムの判断の方が効率的なため)。
初心者向け GCの注意点
参照が残ると回収されない
配列やグローバル変数、キャッシュに入れっぱなしのオブジェクトは参照が残り、GCは回収できません。
使い終わったら、上書きして参照を外すか、コレクションから削除します。
これがGC言語での典型的な「リーク」の原因です。
画像を読み込んで配列に貯めたままにすると、メモリが膨らみ続けます。
スコープを抜けると回収候補になる
関数内の一時変数は、関数が終わって誰からも参照されなくなると回収候補になります。
「スコープを抜けると忘れられる」ことが基本です。
ただし、クロージャやグローバルに渡してしまうと参照が残り続ける点に注意します。
グローバルやキャッシュの長寿命参照に注意
長く生きる入れ物に一時的なデータを入れっぱなしにしないことが大切です。
キャッシュは上限サイズを決め、不要になったら削除する設計にします。
「入れたら必ずどこかで出す」という対になる操作を意識すると安全です。
イベントやタイマーは解除する
イベントリスナーやタイマー(setIntervalなど)は、対象を参照し続けます。
不要になったらremoveやclearで解除しないと、オブジェクトが生き続けてしまいます。
フレームワークが自動解除してくれる場合もありますが、原則は自分で確認します。
大きな配列や画像は参照を外す
メモリを多く使うデータは、使い終わった瞬間に参照を外すと効果が大きいです。
変数の上書きやnull代入、コレクションからの削除を行い、再利用の予定がなければ早めに手放します。
補足
「あとで使うかも」で取り置きしすぎると、GCは回収できずメモリが圧迫されます。
ファイルや接続はGC対象外
ファイルハンドルやソケット、データベース接続などはGCの対象外です。
GCは主にメモリを回収する仕組みで、OSの外部資源は自動では閉じません。
必ずcloseやdispose、withやusingの構文などで明示的に解放します。
具体例
- Python: with open(…) as f: と書くと自動で閉じます。
- Java: try-with-resourcesで自動クローズ。
- C#: using (…) { } ブロックで自動解放。
代表的なGC方式
マークアンドスイープの基本
到達可能なオブジェクトにマークを付け、その後マークのないものを一括で解放する最も基本的な方式です。
実装が比較的簡単で、多くのランタイムの土台になっています。
必要に応じて後段でコンパクションを組み合わせます。
コピーGCのイメージ
メモリを2領域に分け、生きているオブジェクトだけを空いている領域へコピーします。
結果として生存オブジェクトが連続配置になり、確保が非常に速くなります。
一方で、2領域分の空間が必要になる点に注意します。
世代別GCの考え方
「ほとんどのオブジェクトはすぐ死ぬ」という性質を利用し、若い世代と年老いた世代に分けて回収する方式です。
若い世代(短命)は頻繁に素早く、年老いた世代(長命)はたまに時間をかけて回収するため、全体の停止時間を減らしやすくなります。
参照カウントの特徴
各オブジェクトに「何個の参照から指されているか」の数を持ち、0になったら即解放します。
わかりやすく即時解放できる一方、循環参照のように互いに指し合う場合は工夫が必要です。
そのため、参照カウントに加えて別の検出手段を組み合わせることがあります。
以下は各方式のざっくり比較です。
| 方式 | ざっくり動き | 向いている場面 | 注意点 |
|---|---|---|---|
| マークアンドスイープ | 生存にマーク→未マーク解放 | 汎用・大規模アプリ | 一時停止がやや長くなる場合がある |
| コピーGC | 生存オブジェクトを別領域へコピー | 若い世代の回収 | 余分なメモリ領域が必要 |
| 世代別GC | 若い/年老いた世代で分けて回収 | 停止時間を抑えたい一般用途 | 実装が複雑になりがち |
| 参照カウント | 参照数0で即解放 | リアルタイム性が欲しい場面 | 循環参照に弱い |
まとめ
GCは「ルートから辿れるものは残し、辿れないものを回収する」自動メモリ管理の仕組みで、初心者が安心して開発できる強力な土台です。
実際の流れは、メモリ確保→参照の把握→使用中の印付け→未使用の回収→必要なら並べ直し→再開というステップで進みます。
日常開発では、参照を残さない、イベントやタイマーを解除する、不要になった大きなデータは早めに手放す、そして外部資源は必ず明示的に閉じるという基本を守れば十分に安全です。
仕組みの概要と注意点を押さえ、GCの助けを受けながら気持ちよくコードを書いていきましょう。
