基本情報のガベージコレクションは、使わなくなったメモリ領域を自動で回収し、再利用できるようにする仕組みです。回収対象を判断するときは、プログラムが参照をたどって、そのデータへ到達できるかを考えます。
「変数をnullにしたら必ず消える」「参照の矢印があれば必ず残る」と覚えると、複数の変数や循環参照が出た場面で迷います。この記事では参照関係を小さな例に分け、回収できる領域と残す領域を自分で見分ける方法を説明します。
- 変数とデータ本体を分けて参照の矢印を読む
- ルートから到達できない領域を回収対象にする
- 孤立した循環参照と単純な参照カウントを区別する
- コンパクションとの違いを例題で判断する
基本情報のガベージコレクションの仕組み

回収するのは不要になったメモリ
プログラムが扱うデータは、メモリ上に置かれます。処理中に新しいデータを作れば、そのための領域が必要です。一方、役目が終わったデータの領域をずっと確保したままにすると、次のデータに使える空き領域が減ってしまいます。
そこで、動的に確保した領域のうち、不要と判断できるものを回収するのがガベージコレクションです。英語のGarbage Collectionを略してGCと書くこともあります。「回収」とは、その領域を新たな割り当てに使える状態に戻すことです。保存済みのファイルを削除する操作とは分けて考えましょう。
Javaの公式資料でも、生きているオブジェクトから到達できなくなったオブジェクトのメモリを再利用できると説明しています。本記事では、この考え方を試験学習用の参照モデルに置き換えます。根拠はOracleのGC実装資料です。
ヒープは、こうした動的なデータを置く領域の代表例です。なお、アルゴリズムの「ヒープ」という木構造と、メモリ管理の「ヒープ領域」は意味が違います。この先のAやBという文字は、ヒープ上のデータ本体を識別するために使います。
変数とオブジェクトを分けて読む
オブジェクトは、ここではメモリ上にあるデータのまとまりと考えれば十分です。変数aがオブジェクトAを参照する関係を「a→A」と書きます。aは入口、Aはデータ本体です。名前が似ていても、同じものを表しているわけではありません。
「a→A、b→A」なら、aとbは同じAを参照しています。二つの変数があるからといって、Aの本体が二つに増えたわけではありません。参照をコピーすることと、データ本体を複製することの違いが、回収対象を読む最初の分かれ目です。
Aの中に別のオブジェクトBへの参照があれば「a→A→B」と表せます。Bは変数aから直接参照されていませんが、Aを経由してたどれます。直接の矢印だけを見ると、このBを誤って回収対象にしてしまいます。
参照の基本を先に整理したい場合は、基本情報の配列・リスト・ポインタの解説で、データを指す関係を確認するとつながりやすくなります。ここではリストの操作よりも、データ本体が残る条件に注目します。
ルートからたどれるかで判断する

参照をたどり始める入口をルートと呼びます。実装によって具体的な入口は異なりますが、プログラムが使っている変数などが手掛かりになります。問題では、ルートに相当する変数や開始点を先に特定してください。
入口から矢印の向きに沿ってたどれるオブジェクトを「到達可能」、どの入口からもたどれないオブジェクトを「到達不能」とします。到達可能なAの中にBへの参照があればBも到達可能です。Bの中にCへの参照があれば、さらにCも残ります。
このルートと参照の考え方は、Cornell大学の自動メモリ回収の講義資料で確認できます。矢印は片方向です。A→Bがあっても、BからAへ戻れるとは限りません。
学習用に、ルートr→A、A→B、C→Bという関係を考えます。rからAとBへは進めますが、BからCへ進む矢印はありません。CがBを参照していることは、C自身がrから到達できる証拠にはならないのです。回収対象はCになります。
「ほかのオブジェクトから参照されていないものを回収する」という短い説明は、単純な図を読む入口にはなります。ただし、後で出てくる循環や孤立した参照の列を判断するには、参照の有無だけでなく、ルートからの経路を確認する必要があります。
マークと回収の二段階で考える

参照をたどる方式の基本として、マーク&スイープがあります。マークは、ルートから到達できるオブジェクトに印を付ける段階です。スイープは、印が付かなかったオブジェクトの領域を回収する段階です。
ルートr→A→Bと、孤立したCがあるなら、AとBに印が付き、Cには付きません。その後にCの領域が回収されます。「印を付けたものを捨てる」と取り違えないよう、マークは必要なデータを残すための印と考えましょう。
この基本手順はCornell大学のトレース型GCの説明に基づきます。実際の処理系にはさまざまな工夫があるので、すべてのGCが、この単純な二段階の実装だけで動くと覚える必要はありません。
手書きで解くときも同じです。回収しそうなものを感覚で選ぶより、まず残すものをすべてチェックし、最後にチェックのないものを選ぶほうが、複数の入口や枝分かれがある問題に対応できます。
コンパクションとの違い

ガベージコレクションの目的は不要な領域の回収です。これに対してコンパクションは、使っている領域を移動して詰め、分散した空き領域をまとめる処理です。メモリの「圧縮」と表現されますが、画像ファイルなどのデータ圧縮とは区別します。
例えば、空き領域が2単位、3単位、1単位に分かれているとします。合計は6単位ですが、このままでは連続した5単位の領域を取れない場合があります。使用中の領域を移動して空きを6単位にまとめれば、連続した5単位を取りやすくなります。単位と配置は説明用の仮定です。
この例では、使っているデータを捨てていません。並び方を変えているだけです。一方、孤立したデータの領域を回収すれば、再利用できる領域の総量が増えます。問題文が「不要な領域の解放」を述べるのか、「空き領域を連続させる」を述べるのかで見分けます。
GCと同時にコンパクションを行う実装もあります。そのため、同じ処理の流れに出てきても、二つの目的を区別しておくと説明を整理できます。Cornell大学のコンパクションの解説では、回収後に空きが分散する問題と、使用中の領域を移動する方法が説明されています。
| 処理 | 中心となる目的 | 問題文の手掛かり |
|---|---|---|
| ガベージコレクション | 不要なメモリを回収して再利用する | 不要になった領域を自動解放する |
| コンパクション | 分散した空き領域をまとめる | 使用中の領域を移動して連続した空きを作る |
| データ圧縮 | データの表現を変えてサイズを減らす | 圧縮形式や符号化を利用する |
基本情報ではどこまで押さえるか
IPAの基本情報技術者試験シラバスVer.9.2では、主記憶管理の用語例としてガベージコレクションとコンパクションが挙げられています。まず両者が解決する問題と、回収対象の判断を結び付けましょう。ここで作る例題は理解確認用のオリジナル問題で、IPAの過去問そのものではありません。
Javaの細かな起動オプションや、実行環境ごとのGC選択は、この判断とは別の実務テーマです。試験学習の段階では、特定の言語の設定を先に暗記するより、同じ参照関係なら同じ理由で判断できることを目指します。
Javaをどこまで学ぶか迷う場合は、基本情報でJavaを学ぶ必要性と科目Bの関係を確認してください。また、OSのプロセスやスレッドとの関係は、基本情報のOSとスケジューリングの解説で学べます。ここからはメモリの参照関係だけに絞って練習します。
ガベージコレクションの回収対象を例題で見分ける

以下の例題は、参照をたどるGCを用いる学習用モデルです。図や文章に示した変数だけをルートとし、矢印は通常の強い参照として扱います。隠れた参照、弱参照、ファイナライズ、並行処理中の変化は考えません。問うのは「回収対象になるか」で、実際に回収される瞬間ではありません。
参照を一つ消した場合を判定する
例題:最初にa→A、b→Aとなっています。その後、a←nullを実行しました。Aは回収対象になるでしょうか。代入記号の左側にあるaは変数、nullは参照先がないことを表します。
答えは、Aは回収対象になりません。aの矢印は消えますが、b→Aが残るからです。bをルートとしてAへ到達できるので、必要なデータとして残ります。aとbのどちらが最初にAを参照したかは、この判定に影響しません。
続けてb←nullを実行し、ほかにAへの経路がなければ、Aは回収対象になります。変数を一つ変更したかどうかより、変更後の参照関係全体を見てください。変数名が問題の文章から消えたことと、データ本体へのすべての入口が消えたことは別です。
途中の状態を二列に分けてメモすると、読み間違いを減らせます。a←nullの後は「aは参照なし、bはAを参照」、b←nullの後は「aもbも参照なし」です。代入をまとめて読まず、一つずつ矢印を書き換えるのがコツです。
新しいデータへ代入した場合
例題:a→A、b→Aの状態で、新しいオブジェクトBを作り、a←Bとします。変化した後、AとBのどちらが回収対象になるでしょうか。AとBは別の本体で、ほかの参照はないものとします。
答えは、どちらも回収対象になりません。変化後はa→B、b→Aです。aが新しい参照先へ変わっても、bまで自動的にBへ切り替わるわけではありません。AにもBにも、それぞれルートからの入口が残っています。
もし最初からbが存在せず、a→Aだけだったなら、a←BによってAは回収対象になります。Bはaから到達できるので残ります。「新しい本体を作った」と「以前の本体を解放した」を、同じ操作だと扱わないようにしましょう。
また、a←Bは参照の付け替えです。Aの中身を書き換える操作ではありません。「aが指すデータの値を変更した」という問題なら、Aへの矢印がそのまま残る可能性があります。設問が参照先の変更と値の変更のどちらを述べているかを読み分けてください。
間接参照と矢印の向きを読む
例題:ルートはrとsです。関係はr→A、A→B、B→C、s→C、D→Bとします。この状態でr←nullを実行した後、A・B・C・Dの回収対象を求めます。ほかの参照はありません。
答えはA・B・Dです。rの入口がなくなり、残るルートはsだけになります。sからはCへ到達できますが、C→Bという矢印はないのでBへ戻れません。BがCを参照していても、その逆向きに進めるとは限らないのです。
D→Bは残っていますが、Dへ到達する入口もありません。DとBの間に参照があることだけで、両者を残す理由にはなりません。入口のないグループの内部に矢印があっても、ルートからたどれなければ回収対象になります。
rを変更する前の状態なら、r→A→B→CがたどれるためA・B・Cは残り、Dだけが回収対象です。変化前と変化後を比較するときは、同じ判断規則を使い、入口の増減だけを反映してください。
循環参照は外からの入口を見る

例題:A→B、B→C、C→Aという輪があります。最初はr→Aもありますが、その後r←nullを実行しました。ルートはrだけで、ほかの入口はありません。A・B・Cは回収対象になるでしょうか。
参照をたどるGCでは、A・B・Cはすべて回収対象です。三つの本体は互いを参照していますが、rから輪へ入る経路がなくなっています。輪の内部だけでつながっていても、プログラムの入口からは利用できません。
逆に、s→Cという別の入口が残っていれば、C→A→Bと輪全体をたどれます。その場合は三つとも残ります。「循環だから回収されない」ではなく、「その循環へルートから入れるか」で判断します。
これは、部屋に置いた三つの箱をひもでつなぐ教材模型に例えると理解しやすいです。三つの箱を互いにつなぐだけでは、入口から箱へ伸びるひもはできません。画像はこうした関係を説明するための模型イメージで、実際のメモリ内部を撮影したものではありません。
単純な参照カウントとの違い
参照カウントは、各オブジェクトへ入ってくる参照の数を管理し、その数が0になったものを解放する方法です。自動メモリ管理の方法の一つですが、ルートから経路をたどる方式と、同じ判定手順ではありません。
先ほどのA→B→C→Aという輪では、外側のr→Aを消しても、A・B・Cには輪の内部からそれぞれ1本の参照が残ります。単純な参照カウントだけなら、どの数も0にならないので、この輪を回収できません。
一方、ルートからたどる方式では、輪への入口がないことを判断できます。方式の違いはCornell大学の参照カウントの講義資料で確認できます。設問に「参照カウントが0」「ルートから到達不能」とあれば、その条件に合わせて解いてください。
「参照カウントを使う言語は、どんな循環も回収できない」と一般化するのも避けます。例えばPythonの公式文書は、参照カウントを補う循環参照向けのGCを説明しています。実装では複数の仕組みを組み合わせることがあります。根拠はPythonのgcモジュール文書です。
| 学習用の方式 | 回収判断の手掛かり | 孤立した循環 |
|---|---|---|
| 参照をたどる方式 | ルートから到達できるか | 到達不能なら回収対象 |
| 単純な参照カウント | 入ってくる参照の数が0か | 内部の参照が残り、0にならない |
| 循環検出を併用する実装 | 併用している仕組みの条件 | 単純方式だけの場合とは結果が異なり得る |
複数の経路がある総合例題

総合例題では、ルートをrとs、オブジェクトをA〜Gとします。初期状態は、r→A、A→B、B→C、s→C、D→E、E→D、F→Gです。記載した以外の参照はありません。まず、変更前に回収対象になるものを考えてください。
変更前の回収対象はD・E・F・Gです。rからA・B・Cへ到達でき、sからもCへ到達できます。DとEは孤立した輪、FとGは入口のない参照の列なので、どちらのグループもルートからたどれません。
次に、Aが持つBへの参照だけを消します。r→Aとs→Cは残り、B→Cも残るものとします。この変更後の回収対象は、B・D・E・F・Gです。Aはrから、Cはsから到達できるため残ります。
B→Cが残っているからBも残す、という解き方は誤りです。BはCを指していますが、CからBへ逆向きにたどることはできません。Bは入口から切り離されており、残るCを参照していることは、B自身を残す理由になりません。
最後にs→Cも消すと、A以外のB〜Gが回収対象になります。r→Aだけが残り、Aからほかの本体へ進めないためです。このように同じCでも、経路が一本残るか、最後の一本がなくなるかで判断が変わります。
| 判定する時点 | ルートから到達できる本体 | 回収対象 |
|---|---|---|
| 初期状態 | A・B・C | D・E・F・G |
| A→Bだけ削除した後 | A・C | B・D・E・F・G |
| さらにs→Cを削除した後 | A | B・C・D・E・F・G |
この表は到達可能性の判定結果です。「初期状態で回収対象だったDなどが、その後もメモリにあるのはおかしい」と感じる場合は、各行で対象を判定するまで実際の回収は行わないモデルだと読みましょう。実行済みのGCの有無は、問題条件として区別します。
回収対象と回収時刻を分ける

参照が消えた時点で回収対象になっても、その同じ瞬間にメモリが解放されるとは限りません。対象の判定、GCの実行、実際の領域の再利用は、説明上分けて考えると整理しやすくなります。
例題として、時点T1でr→A、時点T2でr←null、時点T3でGCを実行するものとします。ほかの参照がないならT2でAは回収対象です。しかし「T2直後にAの領域は解放済みか」という問いに、参照関係だけで必ずはいとは答えられません。
もし設問が「T3で到達不能な全オブジェクトを回収する」と明記していれば、T3の回収後はAの領域を再利用できると判断できます。反対に、GC実行時刻や回収範囲の条件がなければ、何ミリ秒後に解放されるかや、すぐOSへメモリが返るかまでは求められません。
Java言語仕様にも、到達可能性やその判定点に関する説明があります。実際の言語では最適化などが関係するため、学習用モデルの変数の見た目だけで、実行時の細かな寿命を断定しません。参照元はJava言語仕様の到達可能性の説明です。
GCがあっても残るデータに注意
業務上は使い終わったデータでも、長く生きるリストや管理用の変数から参照され続けていれば、到達可能なままです。例えば、処理済みの注文データAを履歴リストLに追加し、ルートr→L→Aがずっと残る状態を考えます。
このモデルでは、別の一時変数aをnullにしてもAは回収対象になりません。r→L→Aという経路が残るからです。不要なデータが意図せず保持され続ける問題は、GCがあるだけで自動的に解決するとは限りません。
対策を考えるなら、「どの長寿命の本体が、何を参照し続けているか」を調べます。ただし、まだ利用するデータを一律に削除したり、何でもnullにしたりする方法ではありません。業務上必要な保存期間と、メモリ上に保持する期間を分けて設計する考え方が大切です。
また、ファイルやネットワーク接続などの資源は、オブジェクトのメモリとは別に後片付けが必要な場合があります。GCをファイルのクローズなどの代わりと考えないでください。Microsoftの非管理リソースの説明でも、メモリ管理と資源のクリーンアップを区別しています。
選択肢を判断する読み方
用語の説明を選ぶ問題では、何をした結果、どんな状態になるのかに注目します。「不要になった領域を自動で解放する」ならGC、「使用中の領域を移動して空き領域をまとめる」ならコンパクションです。「使い終わったら即座に必ず解放する」と時刻まで限定する文は、方式や条件の確認が必要です。
参照図の問題では、操作後の図を先に完成させます。古い矢印と新しい矢印を頭の中で重ねると、消したはずの経路を残してしまいがちです。変更するのが変数の参照先なのか、オブジェクト内部の参照先なのかも区別してください。
- 示されたルートをすべて書き出す
- 代入やnull化で変わった矢印だけを書き換える
- 各ルートから矢印の向きに沿って到達できる本体を印で残す
- 印のない本体を回収対象として選ぶ
- 回収済みを問う場合はGCの実行条件も確認する
循環に入って同じ本体へ戻ったら、既に印を付けた本体を再びたどる必要はありません。分岐先を全部確認できたかを見るだけで十分です。入口が複数あっても、同じ本体に何度も印を付けず、最後に残る集合として整理しましょう。
読んで分かった後は、自分で矢印を書いてから答えを選ぶ練習が役立ちます。科目全体の確認には、基本情報の科目A・科目Bを無料で演習できる過去問アプリも使えます。この記事の例題を説明できるようになったら、ほかのメモリ管理の用語と区別できるかも確かめてください。
ガベージコレクションの判断まとめ
ガベージコレクションは、不要になったメモリ領域を回収し、再利用できるようにする仕組みです。参照をたどる方式の問題では、ルートから届く本体を残し、届かない本体を回収対象にします。直接参照、間接参照、複数の入口を、同じ規則で判断してください。
孤立した循環は、内部に参照があっても、ルートからの入口がなければ回収対象です。単純な参照カウントでは循環の数が0にならない、という方式の違いも押さえます。そして、領域を回収するGCと、空きをまとめるコンパクションを区別しましょう。
最後に、回収対象になったことと、既に回収されたことを分けて読みます。問題文の入口、矢印の向き、操作後の状態、回収の条件を順に確認すれば、「参照が一つ消えたから全部消える」といった思い込みを避けて解けるようになります。


コメント