本ページはプロモーションが含まれています

Java入門

JavaのGCの仕組みとFull GCが起きる原因を図解【2026年版】

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

「アプリが時々カクつく」「先輩からFull GCが原因と言われたけど何のこと?」は、Javaを学び始めた方や運用担当になったエンジニアからよく聞く相談です。

この記事を読めば、Javaのガベージコレクション(GC)の仕組み・Full GCが起きる原因・GCアルゴリズム4種類・JVM実装5種類・実践的なチューニング手順まで、5分で全体像をつかめます。

特に「Full GCが頻発して止まる」という相談は現場で最も多く、本記事では原因の切り分け方まで踏み込みます。

System.gc()による手動GCの是非や、G1GC・ZGCといった最新GCの違いも2026年8月時点の情報で図解付きで解説します。

私自身、10年以上のJava開発でGC起因のパフォーマンス問題に何度も直面し、チューニングで乗り越えてきました。

その実体験を踏まえ、最短ルートでGCを理解できるように構成しています。

ガベージコレクションとは?

Javaのガベージコレクション(GC)とは何かを示す図解

Javaのガベージコレクション(GC)とは、プログラムが使わなくなったメモリ領域を自動的に見つけて解放する仕組みです。これがあるおかげで、開発者はメモリ管理をJava(JVM)に任せて、アプリケーションのロジック開発に集中できるのです。

Javaにおけるメモリ管理の仕組み

プログラムを動かすには、データやオブジェクトを一時的に保存する場所、つまり「メモリ」が必要です。

Javaでは、プログラムが必要なメモリをOSから確保し、その中でオブジェクトを作成したり、不要になったものを片付けたりしています。

この「片付け」を担当するのがガベージコレクションです。例えるなら、優秀な自動お掃除ロボットのようなもの。

部屋(メモリ)が散らかってきたら(不要なオブジェクトが増えたら)、自動で掃除(メモリ解放)をしてくれます。

おかげで私たちはいつでもきれいな部屋で快適に過ごせ(プログラムを安定して動かせ)ます。

C言語との違い(手動管理との比較)

一方、C言語のようなプログラミング言語では、このような自動お掃除ロボットは存在しません。

開発者自身が、メモリの確保(malloc)と解放(free)をコードに明記する必要があります。

これは非常に手間がかかる上、「解放漏れ」や「二重解放」といったメモリ関連のバグを生み出しやすい、とても神経を使う作業でした。

解放漏れが起きるとメモリリークとなり、アプリケーションが最終的にクラッシュする原因にもなります。

Javaのガベージコレクションは、このような手動メモリ管理の複雑さや危険性から開発者を解放してくれる、画期的な仕組みなのです。

ただし、「自動だから万能」というわけではありません。GCはアプリケーションを瞬間的に停止させる(Stop The World)副作用を持ちます。

そのためミリ秒単位のレイテンシが命のシステムでは「Cのほうが楽だった」と言いたくなる場面も実際にあります。

「自動メモリ管理の対価として停止時間というコストを払っている」という認識を持っておくと、後段のチューニングが格段に理解しやすくなります。

Javaガベージコレクションの仕組み

Javaガベージコレクションの仕組み(ヒープ領域とGC Rootからの参照関係)を示す図解

GCは「ヒープ領域」「GC Root」「参照の有無」という3つの要素で、不要になったオブジェクトを判定します。

それでは、この優秀なお掃除ロボットは、どのようにして「ゴミ」と「まだ使うもの」を見分けているのでしょうか。その仕組みを順番に見ていきましょう。

ヒープ領域とオブジェクトの寿命

Javaでnewキーワードを使って作られたオブジェクトは、すべて「ヒープ領域」と呼ばれるメモリ空間に格納されます。

このヒープ領域が、GCの主戦場、つまりお掃除の対象エリアです。

オブジェクトには「寿命」があります。プログラムのある部分で必要とされて作られても、処理が進むにつれて不要になります。

この「不要になった」状態を、GCはどのように判断するのでしょうか。

ヒープの内部構造(Eden・Survivor・Old)

世代別GCで使われるヒープは、さらに細かく分かれています。オブジェクトの寿命に応じて移動していくのがポイントです。

  • Eden領域: new で生成された直後のオブジェクトが置かれる場所。Minor GC で大半が回収される
  • Survivor領域(S0 / S1): Edenを生き延びたオブジェクトの一時避難所。S0とS1を交互に使ってコピーすることでフラグメンテーションを防ぐ
  • Old領域(Tenured): 一定回数のMinor GCを生き延びたオブジェクトが昇格してくる長期居住エリア

このEden→Survivor→Oldという遷移を「世代別ヒープ」と呼びます。

短命オブジェクトを高速に回収し、長命オブジェクトはまとめて低頻度で処理することで、全体のオーバーヘッドを抑える設計です。

ルート(GC Root)からの参照関係

GCは、まず「GC Root」と呼ばれる特別な起点から、オブジェクトをたどっていきます。

GC Rootは、プログラムが直接アクセスできる場所のことで、例えば実行中のメソッド内にある変数などが該当します。

イメージとしては、家の玄関(GC Root)からスタートして、そこからつながっているすべての家具や物(オブジェクト)を一つひとつ確認していく作業に似ています。

玄関からたどって行き着けるものは「まだ使っているもの」と判断されます。

「参照がない=回収対象」となる流れ

GC Rootからたどる作業を行った結果、どこからもたどり着けなかったオブジェクトが出てきます。

これが「どこからも参照されていないオブジェクト」、つまり「不要になったゴミ」です。

あわせて読む

GCは、このゴミと判断されたオブジェクトが使っていたメモリ領域を解放し、再利用できるようにします。

この一連の流れを繰り返すことで、ヒープ領域は常にクリーンに保たれるわけです。

  1. マーキング: GC Rootからオブジェクトをたどり、生きているオブジェクトに印(マーク)を付ける。
  2. スイープ(回収): 印が付かなかったオブジェクト(ゴミ)を回収し、メモリを解放する。

このシンプルなルールが、Javaの安定した動作を支えています。

主なGCアルゴリズム4選|マーク&スイープ・コピー・世代別・G1GC

Javaの主なGCアルゴリズム4種類(マーク&スイープ・コピー・世代別・G1GC)を比較した図解

GCのアルゴリズムには、基本のMark&Sweepから高速なG1GCまで4種類あり、それぞれ速度とメモリ効率のトレードオフが異なります。

お掃除ロボットにも様々な種類があるように、GCにもいくつかのアルゴリズム(掃除の方法)が存在します。ここでは代表的な4つのアルゴリズムを紹介します。

4つのアルゴリズムを一目で比較

アルゴリズム速度メモリ効率フラグメンテーション主な用途
Mark&Sweep普通発生する基本実装・教科書的
Copying速い×(半分遊ぶ)起きないNew領域
世代別GC速い低い主流アプローチ
G1GC速い・予測可能低い大ヒープ・低停止時間

Mark and Sweep(マーク&スイープ方式)

Mark and Sweepは最も基本的なGCアルゴリズムで、先ほど説明した仕組みそのものです。

ポイント

  1. Mark(マーク): 生きているオブジェクトに印を付ける。
  2. Sweep(スイープ): 印が付いていないオブジェクトを回収する。

シンプルで分かりやすいですが、掃除後にメモリが虫食い状態になる「フラグメンテーション」が起きやすいという弱点があります。

Copying(コピー方式)

メモリ空間を2つの領域に分け、片方だけを使用します。GCが発生すると、生きているオブジェクトだけをもう一方の空いている領域にコピーします。

コピーが終わると、元の領域はすべてゴミだったことになるので、一気にクリアします。

この方式はフラグメンテーションが起きず高速ですが、常に半分のメモリ領域が遊んでいる状態になるため、メモリ効率が悪いのが欠点です。

Generational GC(世代別GC)

多くのアプリケーションでは、「ほとんどのオブジェクトは作られてすぐに不要になる」という統計的な事実があります。この性質を利用したのが世代別GCです。

ヒープ領域を、生まれたばかりのオブジェクトを置く「New(Young)領域」と、何度もGCを生き延びたオブジェクトを置く「Old領域」に分けます。

ポイント

  • New領域: 頻繁にGC(Minor GC)を行い、すぐに不要になるオブジェクトを効率的に掃除する。ここでは高速なコピー方式が使われることが多いです。
  • Old領域: 生き残ったオブジェクトが移動してくる場所。GCの頻度は低く(Major GCまたはFull GC)、マーク&スイープ方式などが使われます。

このハイブリッドなアプローチにより、アプリケーション全体の停止時間を短くし、効率的なメモリ管理を実現しています。

2026年8月時点でも、G1GCやGenerational ZGCをはじめとする主要GCに採用されている主流の考え方です。

G1GC(Garbage First GC)

G1GCは、ヒープ領域を多数の小さな「リージョン」に分割して管理します。

そして、ゴミが最も多く溜まっているリージョンから優先的(Garbage First)に掃除を行います。

大きなヒープ領域でも予測可能な短い停止時間を実現できるように設計されており、Java 9以降のデフォルトGCとして広く使われています。

2026年3月にリリースされたJava 26では、JEP 522によりG1GCの同期処理を減らしてスループットをさらに改善しており、2026年8月時点でも汎用ワークロードのファーストチョイスであり続けています。

JavaのGC実装5種類|Serial・Parallel・CMS・G1GC・ZGCの違い

JavaのGC実装5種類(Serial・Parallel・CMS・G1GC・ZGC)の違いを示す図解

Javaの主なGC実装はSerial・Parallel・CMS(廃止済み)・G1GC・ZGCの5種類で、現在の標準はG1GCです。

これまで見てきたアルゴリズムをベースに、Java(JVM)にはいくつかのGC実装が用意されています。

バージョンアップと共に、より高性能なGCが登場してきました。

2026年3月17日には最新フィーチャーリリースのJava 26がGAとなりましたが、本番環境では引き続きLTSであるJava 25・21・17のいずれかを使うのが一般的です。

あわせて読む

Serial GC

シングルスレッドでGCを行う、最もシンプルな実装です。GC中はアプリケーションが完全に停止(Stop The Worldと呼ばれます)します。

CPUコアが1つしかないような小規模な環境や、クライアントPC上の単純なアプリケーション向けです。

あわせて読む

Parallel GC

複数のスレッドを使ってGC処理を並列実行することで、GC時間を短縮します。特にNew領域のGC(Minor GC)を高速に行うのが得意です。

Java 8までのデフォルトGCで、スループット(単位時間あたりの処理量)を重視するバッチ処理などに向いています。

Java 9以降はG1GCがデフォルトとなり、現在では特殊なスループット重視ワークロード以外で積極的に選ぶ理由は減っています。

CMS(Concurrent Mark-Sweep)GC

アプリケーションの停止時間(Stop The World)を極力短くすることに重点を置いたGCです。

マークやスイープといった処理の一部を、アプリケーションスレッドと同時に(Concurrent)実行しようと試みます。

応答速度が求められるWebアプリケーションで利用されていましたが、Java 9でDeprecated(非推奨)となり、Java 14(2020年)で完全に削除されました

新規プロジェクトでの選択肢にはなりません。

G1GC

前述の通り、ヒープをリージョンに分割して管理するGCです。スループットと応答時間のバランスが良く、Java 9からデフォルトのGCとなりました。

数GBから数十GBといった比較的大きなヒープサイズを扱う現代的なアプリケーションに適しています。

ZGC(最新のGC方式)

ZGCは、Stop The Worldを極限まで短くすることを目指したGCです。

Java 21でGenerational ZGC(世代別ZGC)が追加され、Java 24では非世代モードが削除されました。

Java 25(2025年9月リリースのLTS)以降は、Generational版のみが有効です。

数TB(テラバイト)級の巨大なヒープサイズでも停止時間をサブミリ秒に抑えられます。

一方で、G1GCに比べてメモリを15〜30%、CPUを5〜10%多く消費する点は留意が必要です。

応答性能が非常にクリティカルでリソースに余裕がある大規模システム向けのGCです。

実務でのGC選定基準|私が現場で使い分ける判断軸

5種類のGCを実装上知っているだけでは現場では使えません。10年以上のJava開発で、私が実際にGCを選ぶときに使っている判断軸はシンプルです。

  • ヒープ4GB未満・スループット重視のバッチ: -XX:+UseParallelGC。CPUを使い切ってでも処理時間を縮めたいときの選択。
  • ヒープ4〜32GB・汎用Webアプリ: G1GC(Java 9+のデフォルト)。何も指定しないが正解の領域。
  • ヒープ32GB超・低レイテンシSLO(p99 100ms未満): -XX:+UseZGC(Generational ZGC)。CPU・メモリのオーバーヘッドを許容できるなら最有力。
  • コンテナで<512MB: Serial GC(-XX:+UseSerialGC)。マイクロサービスのCPU/メモリ食い荒らしを防ぐ目的でJVMオプションに固定する。

「最新だからZGC」「速いからParallel」と単独要素で選ぶのは危険です。本番投入後にp99レイテンシ悪化やコンテナOOMKillに悩まされます。

ヒープサイズ × SLO(停止時間/スループット)× リソース余裕の3軸で決めるのが結局いちばん事故が少ない、というのが現場の感覚です。

GC変更は設定を1行変えるだけの軽い作業に見えますが、実際には監視ダッシュボードやアラート閾値の見直しまで含めた地味な作業が伴います。

変更前に「何を根拠に良し悪しを判断するか」を決めておくと、後戻りが少なくて済みます。

GCチューニング実践|ヒープサイズ調整と手動GC(System.gc())の使い方

Javaのヒープサイズ調整とGCチューニングの実践手順を示す図解

GCは自動で動きますが、時にはアプリケーションの特性に合わせて設定を調整(チューニング)することで、パフォーマンスが大きく改善します。

GCの動作をログで確認する方法|-Xlog:gcで可視化

まずは、GCがどのように動いているかを知るのが第一歩です。Javaの起動オプションに以下を追加すると、GCの動作がログに出力されます。

-Xlog:gc

このログを見ることで、GCの発生頻度や、一回のGCにかかった時間、メモリの解放量などを確認できます。パフォーマンス問題の調査には不可欠な情報です。

Java 9以降の統一ロギング(Unified Logging)形式で、詳細を出すなら -Xlog:gc*=info、ファイル出力なら -Xlog:gc*:file=gc.log のように指定します。

ヒープサイズを調整する|-Xms/-Xmxの設定方法

最も基本的で効果的なチューニングが、ヒープサイズの設定です。

  • -Xms<size>: ヒープ領域の初期サイズ
  • -Xmx<size>: ヒープ領域の最大サイズ

例えば、初期サイズを1,024MB、最大サイズを2,048MBに設定する場合は以下のようになります。

-Xms1024m -Xmx2048m

一般的に、サーバーアプリケーションでは-Xms-Xmxを同じ値に設定することが推奨されます。

実行中にヒープサイズが変動する際のオーバーヘッドをなくし、パフォーマンスを安定させられます。

手動GC(System.gc())は使うべき?

Javaには System.gc() を呼び出してGCを明示的に要求する方法がありますが、通常のサーバーアプリケーションでは推奨されません。理由は3つあります。

  • あくまで「要請」: System.gc() はJVMにGCの実行を依頼するだけで、即時実行は保証されません。
  • Stop The Worldを誘発: 自動GCが最適化したタイミングを乱し、応答性能を悪化させます。
  • 不要な負荷: 多くの場合JVMの方が賢く判断するため、人間の介入は逆効果になります。

例外的に有効なのはバッチ処理の境界やヒープダンプ取得直前などの限定用途です。

サービスでの濫用を防ぐには、JVMオプション -XX:+DisableExplicitGCSystem.gc() を無効化する選択肢もあります。

メモリリークが起きる原因|GCがあっても防げない理由

「GCが自動でメモリを掃除してくれるなら、メモリリークは起きないのでは?」と思われるかもしれませんが、実際にはJavaアプリケーションでもメモリリークは頻繁に発生します。

原因はGCの不具合ではなく、「参照が残っているために回収できない」状態がコード側で作られてしまうことです。

  • staticフィールドへの溜め込み: staticMap やキャッシュにオブジェクトを追加し続け、削除処理を書き忘れるケース。アプリケーション終了までGC Rootから参照され続けるため回収されない。
  • リスナー・コールバックの解除漏れ: イベントリスナーやコールバックを登録したまま解除しないと、登録先のオブジェクトが本来不要になったあとも参照が残り続ける。
  • ThreadLocalの後始末忘れ: スレッドプール環境でThreadLocalに値を設定したままremove()し忘れると、スレッドが使い回されるたびに古い値が残り続ける。
あわせて読む

いずれもGC自体は正しく動作しており、「まだ参照されているから回収できない」だけです。

疑わしいときは、前述のjcmd <pid> GC.heap_dumpでヒープダンプを取得し、どのオブジェクトが大量に残っているかを確認するのが最短の切り分け方法です。

チューニングの基本的な考え方

GCチューニングは奥が深い世界ですが、基本はシンプルです。

  1. 現状把握: まずはGCログを取得し、現状のGCの振る舞いを正確に把握します。
  2. ボトルネック特定: GCの時間が長すぎるのか、それとも頻度が多すぎるのか、問題点を特定しましょう。
  3. 仮説と検証: ヒープサイズやGCアルゴリズムの変更といった対策を立て、実際に試して効果を測定します。

やみくもな設定変更は、かえって状況を悪化させます。

根本的な解決策は、不要なオブジェクトの生成を抑えるプログラムを書くことである、という点も忘れてはいけません。

GCチューニングでハマりがちな3つの落とし穴

「とりあえずヒープを大きくする」「ZGCにすれば解決する」。10年以上の現場経験で何度も見かけたアンチパターンを3つ紹介します。

  1. ヒープを増やしてもFull GCが減らない: 原因は static Mapやキャッシュへのオブジェクト溜め込み(メモリリーク)。jcmd <pid> GC.heap_dump で確保元を確認するのが先決。
  2. ZGCに切り替えたらレイテンシは改善したがスループットが落ちた: ZGCはCPUオーバーヘッドが5〜10%増える。バッチ系・CPUバウンドなワークロードでは逆効果。SLOがレイテンシ寄りかスループット寄りかで選ぶ。
  3. コンテナでJVMがメモリを食い潰してOOMKill: -Xmx をコンテナのメモリ上限と同じに設定するとMetaspaceやスレッドスタックで超過。コンテナ上限の70〜75%を目安に -Xmx を設定するのが鉄則。

実際に私が経験したケースでは、あるバッチ処理でSpring Batchのchunk sizeを1000に設定していたところ、本番投入後にヒープが急増しFull GCが頻発、最終的にOOMで処理が落ちるという障害が起きました。

原因調査には2時間ほどかかりましたが、chunk sizeを100に絞る修正はわずか5分で完了。

GC設定をいじる前に「そもそも一度にヒープへ載せているデータ量が適切か」を疑うべきだった、という教訓です。

どれも「GCログを見ずに勘で設定を変えた」ときに起きます。

-Xlog:gc* でログを取り、GCeasy のような可視化ツールで分析するワンステップを挟むだけで、ほとんどの事故は防げます。

Javaガベージコレクションのよくある質問(FAQ)

GCはいつ実行されますか?

JVMがヒープ使用率や残メモリ量から自動判断します。Eden領域が満杯になればMinor GC、Old領域の使用率がしきい値を超えればMajor GC(Full GC)が走ります。System.gc() による明示制御は非推奨です。

Full GCとMinor GCの違いは?

Minor GCはNew(Young)領域のみを対象とし、短時間で完了します。一方Full GCはOld領域・Metaspaceを含む全ヒープを対象とし、停止時間(Stop The World)が秒単位になることもあります。Full GC が頻発するアプリは要チューニング対象です。

Java 21以降のおすすめGCは?

2026年8月時点で、汎用WebアプリケーションはG1GC(デフォルト)、低レイテンシ・大ヒープ要件ならGenerational ZGC(Java 21+)が第一候補です。Java 17以降ではParallel GCやCMSは特殊用途以外で選ぶ必要はほぼありません。

OutOfMemoryErrorはGCの問題ですか?

多くはメモリリーク(参照を握り続けている)ヒープサイズ不足が原因で、GC自体の不具合ではありません。-Xmx の見直しと、jmapjcmd でのヒープダンプ解析がまず行うべき切り分け手順です。

GCアルゴリズムには何種類ありますか?

代表的なGCアルゴリズムは4種類です。基本のMark&Sweep、高速だがメモリ効率の悪いCopying、両者を組み合わせた世代別GC、そして大ヒープ向けのG1GCがあります。現在はG1GCをベースにした世代別アプローチが主流です。

Serial GCとParallel GCの違いは?

Serial GCはシングルスレッドでGCを行うため小規模環境向け、Parallel GCは複数スレッドで並列実行しGC時間を短縮するためスループット重視のバッチ処理向けです。Java 9以降はどちらもデフォルトではなく、G1GCが標準です。

GCとメモリリークの関係は?

GCは正しく動作していてもメモリリークを防げません。staticフィールドへの溜め込みやリスナーの解除漏れなど、コード側で参照が残り続けるとGCは回収対象と判断できないためです。疑わしいときはヒープダンプで確保元を確認します。

まとめ

この記事では、Javaのガベージコレクションの基本的な仕組みから、アルゴリズムの種類、そして実践的なチューニングの第一歩までを解説しました。

Java GCを理解する3つのメリット

JavaのGCを理解することは、エンジニアにとって大きな力になります。

  1. パフォーマンス問題の原因究明: アプリケーションが遅い時、GCが原因かどうかを切り分けられるようになります。
  2. メモリ効率の良いコード: GCの仕組みを知ることで、メモリに優しい、より効率的なコードを書く意識が芽生えます。
  3. 適切なJVM設定: アプリケーションの特性に合わせた、適切なヒープサイズやGCアルゴリズムを選択できるようになります。
  • この記事を書いた人
  • 最新記事

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

-Java入門