Java

Javaのガベージコレクション(GC)とは|仕組み・種類・チューニングの基礎

Javaのガベージコレクション(GC)は、プログラムが使わなくなったオブジェクトをJVMが自動で見つけてメモリを解放する仕組みです。C言語のような手動解放が不要になる一方、GCが動く間はアプリケーションが一瞬止まるため、種類の選択とログの読み方を知らないまま本番運用すると、応答遅延の原因が特定できなくなります。この記事では、GCの動作原理(マークアンドスイープ)、フルGCとマイナーGCの違い、Serial/Parallel/G1/ZGC/Shenandoahの選び方、-Xlog:gcでのログ確認、そしてSystem.gc()を呼ぶべきかまでを、2026年6月時点のJavaの最新仕様にそって整理します。

まとめ:Java GCで最初に押さえる5点

結論を先に示します。詳細は各セクションで根拠とともに解説します。

  • GCは「到達できないオブジェクト」を回収する。参照が残っていれば回収されないため、不要になった参照をnullにするかスコープを抜けることが回収の前提になる。
  • GCには短時間で頻発するマイナーGC(Young領域のみ)と、ヒープ全体を対象に長く止まるフルGCがある。チューニングの主目的はフルGCの停止時間と頻度を抑えること。
  • 2026年時点の既定GCはG1。Java 14でCMSは削除済みなので、古い記事のCMS前提の設定はそのまま使えない。
  • 大きなヒープで停止時間を最優先するならZGCまたはShenandoah(どちらもJDK 15で正式版)。スループット重視はParallel。
  • GCログはJDK 9以降の統合ログ-Xlog:gcが標準。-Xloggc-XX:+PrintGCDetailsは非推奨で、新規導入では使わない。

ガベージコレクション(GC)とは|Javaが自動でメモリを回収する仕組み

ガベージコレクション(Garbage Collection)とは、ヒープ上で参照されなくなったオブジェクトを検出し、その領域を自動的に再利用可能にするJVMの機能です。Javaではnewで生成したオブジェクトはヒープに置かれますが、開発者が明示的に解放することはできません。代わりにGCが「もうどこからも使われていない」と判断したオブジェクトを回収します。

目的は2つあります。1つはメモリリークや二重解放といった、手動メモリ管理で頻出するバグを構造的に防ぐこと。もう1つは長時間稼働するアプリケーションでヒープを使い続けられるようにすることです。ただしGCは無料ではありません。回収処理のあいだアプリケーションスレッドが止まる「ストップ・ザ・ワールド(STW)」が発生し、これが応答遅延として表面化します。GCを理解する目的は、この停止をどう小さく・予測可能にするかに尽きます。

GCの動作原理|マークアンドスイープと到達可能性

JavaのGCの土台はマークアンドスイープです。GCルート(スレッドのスタック変数、静的フィールド、JNI参照など)から参照をたどり、たどり着けたオブジェクトを「生存」とマークし、マークされなかったオブジェクトをスイープ(回収)します。「使用回数」ではなく「GCルートから到達できるか」で判定する点が重要で、循環参照していても双方が到達不能なら両方回収されます。

到達可能性で判定するから、参照が残ると回収されない

逆に言えば、不要になったのに参照が残っているオブジェクトはGCの対象になりません。よくあるのは、staticなコレクションに要素を追加し続けてremoveを忘れるケースです。GCは正しく動いていてもヒープが増え続け、最終的にOutOfMemoryErrorに至ります。これは後述の「GCがあってもメモリリークは起きる」で詳しく扱います。

世代別GCとヒープ構造(Young領域とOld領域)

HotSpot JVMのヒープはYoung領域(Eden+2つのSurvivor)とOld領域に分かれます。これは「多くのオブジェクトはすぐ死ぬ」という経験則(弱い世代仮説)に基づく設計です。新しいオブジェクトはEdenに作られ、マイナーGCを生き延びるとSurvivorを経てOldへ昇格します。短命なオブジェクトをYoungだけで安く回収できるため、ヒープ全体を毎回スキャンするより効率的です。なお、メソッドのローカル変数や呼び出しフレームを管理するスタックはGCの対象外で、ヒープとスタックの役割分担はJavaヒープとCヒープ、およびスレッドスタックのメモリ空間の使い分けと役割で整理しています。

GCが動くタイミングとトリガー条件

GCの実行タイミングはアプリケーションから明示的に制御するものではなく、JVMがヒープの状況を見て自動的に判断します。マイナーGCはEdenが満杯になったとき、フルGC(またはOld領域の回収)はOld領域が逼迫したときや、昇格できる空きが確保できないときに発生します。System.gc()を呼ぶとGCを「要求」できますが、実行は保証されず、タイミングを指定する用途には使えません。

「いつ動くか」を後から把握する唯一の確実な方法はGCログです。推測でなく、ログでマイナーGC/フルGCの発生間隔と停止時間を実測してから対策を決めます。

フルGCとマイナーGCの違い

運用で最初に切り分けるべきは、起きているのがマイナーGCかフルGCかです。両者は対象範囲・頻度・停止時間がまったく異なります。

項目 マイナーGC フルGC
対象 Young領域 ヒープ全体(Young+Old)
頻度 高い(数秒〜数分ごと) 低い(理想は稀)
停止時間 短い(ミリ秒級) 長い(数百ミリ秒〜秒級)
主なトリガー Edenが満杯 Old領域の逼迫・昇格失敗
運用上の注意 頻度が高くても通常は許容 頻発・長時間化は要対策

マイナーGCは頻繁に起きても短いため、多くの場合そのまま許容できます。問題になるのはフルGCです。フルGCが数秒単位で止まる、あるいは短い間隔で連発する場合は、Old領域へのオブジェクト昇格が多すぎる(=不要に長生きするオブジェクトがある)か、ヒープが小さすぎるサインです。フルGCの発生条件は「Oldが埋まったとき」なので、対策はOldに溜まる量を減らすか、ヒープと使用GCを見直すかの二択になります。

JavaのGCの種類と選び方(Serial/Parallel/G1/ZGC/Shenandoah)

現行のHotSpot JVMで実用上選べるGCは次の5つです。ここは情報が古くなりやすい箇所で、かつてよく解説されたCMS(Concurrent Mark Sweep)はJava 9で非推奨化(JEP 291)、Java 14で削除(JEP 363)されており、2026年現在の選択肢には含まれません。

GC 有効化フラグ 向くワークロード 備考
Serial -XX:+UseSerialGC 小ヒープ・単一CPU・短命プロセス 単一スレッドで単純
Parallel -XX:+UseParallelGC スループット最優先のバッチ 停止時間より処理量重視
G1 -XX:+UseG1GC 汎用・中〜大ヒープ Java 9以降の既定
ZGC -XX:+UseZGC 大ヒープで低レイテンシ JDK 15で正式版
Shenandoah -XX:+UseShenandoahGC 低・安定した停止時間 JDK 15で正式版

特別な理由がなければ、既定のG1のまま始めます。G1は停止時間の目標値(-XX:MaxGCPauseMillis)を与えてバランスを取る設計で、大半のWebアプリは追加設定なしで十分機能します。バッチ処理のように総処理時間だけが指標で停止が許容できるならParallel。ヒープが数十GB規模で、応答時間のばらつきを抑えたいならZGCShenandoahを検討します。

ZGCはJDK 21で世代別(Generational ZGC, JEP 439)に拡張され、JDK 23で世代別が既定化(JEP 474)、JDK 24で非世代モードが削除(JEP 490)されました。つまりJDK 24以降でZGCを使う場合は常に世代別が動くため、古い記事にある-XX:+ZGenerationalの付与は新しいJDKでは不要・非対応になっている点に注意してください。なお最新LTSのJava 25では、従来Serialにフォールバックしていた制約環境でもG1が標準化されています(JEP 523)。利用中のJDKでの既定や対応フラグは、バージョン更新のたびに公式リリースノートで確認するのが安全です。Javaのバージョン移行全体の流れはJava 21からJava 25へのアップデート概要にまとめています。

GCログの見方と確認方法(-Xlog:gc)

GCの挙動は推測せず、ログで実測します。JDK 9以降はGCログを含むJVMログが統合ログ(Unified Logging, JEP 158/271)に一本化され、-Xlogで制御します。最小構成は次のとおりです。

java -Xlog:gc -jar app.jar

停止時間や原因まで詳しく残し、ファイルに出力するなら次のように指定します。

java "-Xlog:gc*:file=gc.log:tags,uptime,level" -jar app.jar

出力される各行には、発生したGCの種別(Young/Full など)、回収前後のヒープ使用量、所要時間が含まれます。読むときの着眼点は3つです。フルGCの頻度(短間隔で連発していないか)、1回あたりの停止時間(応答SLAを超えていないか)、回収後にOld使用量が下がっているか(下がらなければリークの疑い)。-Xloggc-XX:+PrintGCDetailsといった旧フラグはJDK 9で多くが削除・整理されており(JVMが起動を拒否する場合もあります)、新規導入では使わず-Xlogに統一します。出力したログを視覚化したい場合は、GCViewerやGCeasyといったGCログ解析ツールで停止時間の分布やヒープ推移をグラフ化できます。JVMのデバッグ系コマンドについてはJDBとは何か?Javaデバッガの基本と役割もあわせて参考にしてください。

System.gc()を手動で呼ぶべきか

結論から言うと、アプリケーションコードでSystem.gc()を呼ぶべきではありません。これは仕様上「GCの実行を要求する」だけで、JVMが無視することも、要求を受けてヒープ全体のフルGCを走らせて長い停止を招くこともあります。タイミングも回収範囲も制御できないため、性能改善の手段にはなりません。実際、起動オプション-XX:+DisableExplicitGCで明示的なGC要求を無効化する運用も一般的で、これは「呼んでも効かない・むしろ害になりうる」という現場の判断を反映しています。

例外的に意味があるのは、ベンチマーク測定の前にヒープ状態を揃えたい場合や、RMIの分散GCのように内部実装が利用する場合に限られます。通常の業務コードでメモリ不足を感じたら、System.gc()ではなく、不要な参照を切る・キャッシュに上限を設ける・ヒープサイズとGC種類を見直す、の順で対処します。

GCがあってもメモリリークは起きる

「JavaはGCがあるからメモリリークしない」という理解は誤りです。GCは到達不能なオブジェクトしか回収しないため、論理的には不要なのに参照が残っているオブジェクトは回収されず、ヒープを圧迫し続けます。Java特有のリークには、はっきりした失敗パターンがあります。

  • staticコレクションへの追加忘れ:アプリ生存期間中ずっと参照が残るため、要素が永久に回収されない。
  • リスナー・コールバックの解除漏れ:登録したオブジェクトが解放されず、関連するオブジェクトごと残る。
  • 独自キャッシュの上限なし運用:エントリが増え続け、Old領域に蓄積してフルGCを誘発する。

見分け方は明確です。GCログでフルGCの直後もOld使用量が下がらず右肩上がりなら、回収できないオブジェクトが溜まっているリークの典型です。対策は、ライフサイクルを意識して不要な参照を確実に切ること、キャッシュにはWeakHashMapや明示的な上限・有効期限を設けることです。GCはメモリ管理を肩代わりしますが、参照の設計まで肩代わりはしないという前提で書くことが、リークを防ぐ唯一の方法です。GCとメモリ管理全体の基礎はガベージコレクションとは何か?メモリ管理の基本的な仕組みとその重要性でも解説しています。

よくある質問(FAQ)

GC(ガベージコレクション)とは何ですか?

プログラムが使わなくなったオブジェクトをJVMが自動で検出し、ヒープのメモリを再利用可能にする仕組みです。手動でのメモリ解放が不要になり、メモリリークや二重解放といったバグを構造的に減らせます。一方でGC実行中はアプリケーションが一時停止するため、種類選択とログ確認が運用上の鍵になります。

フルGCとは何ですか?マイナーGCとどう違いますか?

フルGCはYoung領域とOld領域を含むヒープ全体を対象にする回収で、停止時間が長く(数百ミリ秒〜秒級)、Old領域が逼迫したときに発生します。マイナーGCはYoung領域だけを対象にする短時間の回収です。マイナーGCは頻発しても許容できることが多く、対策が必要になるのは主にフルGCの長時間化・連発です。

GCのタイミングは指定できますか?

アプリケーションから実行タイミングを指定することはできません。System.gc()で要求は出せますが実行は保証されず、性能制御には使えません。実際の発生間隔や停止時間は-Xlog:gcのログで実測し、それを基にヒープサイズやGC種類を調整します。

どのGCを選べばよいですか?

特別な要件がなければ既定のG1で始めます。総処理量を最優先するバッチ処理ならParallel、数十GB級のヒープで応答時間のばらつきを抑えたいならZGCかShenandoahが候補です。CMSはJava 14で削除されているため選べません。

C#のガベージコレクションとJavaのGCは同じですか?

「不要オブジェクトを自動回収する」「世代別である」という基本思想は共通ですが、実装は別物です。C#(.NET)とJava(JVM)ではGCの種類・チューニングフラグ・ログ形式が異なるため、設定値や運用手順をそのまま流用することはできません。本記事の具体的なフラグやログはJVM(HotSpot)前提です。

関連記事

資料請求

RELATED POSTS 関連記事