メインコンテンツまでスキップ

ガベージコレクション

はじめに

ガベージコレクション(GC)は、不要になったオブジェクトを自動的に回収し、メモリを解放する仕組みです。Javaプログラマーは手動でメモリを解放する必要がありませんが、GCの仕組みを理解することでパフォーマンスを最適化できます。


GCの基本概念

到達可能性(Reachability)

GCは「到達可能性」に基づいてオブジェクトの生死を判断します。

┌─────────────────────────────────────────────────────────────────────┐
│ 到達可能性の判定 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ GC Roots(起点) │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ・スタック上のローカル変数 │ │
│ │ ・static 変数 │ │
│ │ ・JNI参照 │ │
│ │ ・アクティブなスレッド │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Object A │───→│Object B │───→│Object C │ 到達可能 → 生存 │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │Object D │───→│Object E │ 到達不能 → 回収対象 │
│ └─────────┘ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

GCアルゴリズムの種類

アルゴリズム動作特徴
Mark-Sweepマーク後にスイープシンプルだがフラグメント発生
Mark-Compactマーク後に圧縮フラグメント解消、コスト高
Copying生存オブジェクトをコピー高速、メモリ効率50%
Generational世代別に異なる戦略現代のGCの基本

世代別GC

なぜ世代別か?

弱い世代仮説(Weak Generational Hypothesis)

  • ほとんどのオブジェクトは若くして死ぬ
  • 長生きしたオブジェクトは更に長生きする傾向がある
┌─────────────────────────────────────────────────────────────────────┐
│ オブジェクトの生存率 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 生存率 │
│ 100% ┤ │
│ │█ │
│ │█ │
│ 50% │█ │
│ │█ █ │
│ │█ █ █ │
│ 0% │█ █ █ █ █ █ █ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ └──────────────────────────────────────────→ 経過時間 │
│ ↑ ↑ │
│ │ │ │
│ ほとんどが 長生きするものは │
│ すぐ死ぬ さらに長生き │
│ │
└─────────────────────────────────────────────────────────────────────┘

Young Generation のGC(Minor GC)

Minor GC の流れ
═══════════════════════════════════════════════════════════════════════

1. GC前
┌──────────────────────────────────────────────────────────────┐
│ Eden │ Survivor0 │ Survivor1 │
│ ████████████████████ │ ██ │ │
│ (新規オブジェクト) │ (前回生存) │ (空) │
└──────────────────────────────────────────────────────────────┘

2. Mark & Copy
┌──────────────────────────────────────────────────────────────┐
│ Eden → 生存オブジェクトを S1 へコピー │
│ S0 → 生存オブジェクトを S1 へコピー(年齢+1) │
└──────────────────────────────────────────────────────────────┘

3. GC後
┌──────────────────────────────────────────────────────────────┐
│ Eden │ Survivor0 │ Survivor1 │
│ │ │ ███ │
│ (空) │ (空) │ (生存オブジェクト) │
└──────────────────────────────────────────────────────────────┘

4. 次回は S0 と S1 の役割が入れ替わる

Old Generation への昇格(Promotion)

┌─────────────────────────────────────────────────────────────────────┐
│ 昇格(Promotion)の条件 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 年齢(Age)が閾値に達した │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ デフォルト: 15回のMinor GCを生き延びると昇格 │ │
│ │ 設定: -XX:MaxTenuringThreshold=15 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 2. Survivor領域がオーバーフロー │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Survivor に入りきらないオブジェクトは直接 Old へ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 3. 大きなオブジェクト │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Eden に入りきらない巨大オブジェクトは直接 Old へ │ │
│ │ 設定: -XX:PretenureSizeThreshold │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

Major GC / Full GC

種類対象トリガー
Minor GCYoung Generation のみEden が満杯
Major GCOld Generation のみOld が一定割合に達した
Full GC全世代 + Metaspace明示的呼び出し、Metaspace満杯等

GCコレクターの種類

Java 21で利用可能なGC

┌─────────────────────────────────────────────────────────────────────┐
│ GCコレクター比較 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Serial GC │ │
│ │ -XX:+UseSerialGC │ │
│ │ ・シングルスレッド │ │
│ │ ・小規模アプリ、クライアント向け │ │
│ │ ・Stop-The-World 時間: 長い │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Parallel GC │ │
│ │ -XX:+UseParallelGC │ │
│ │ ・マルチスレッド │ │
│ │ ・スループット重視 │ │
│ │ ・Stop-The-World 時間: 中程度 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ G1 GC (Garbage-First) ★ Java 9以降デフォルト │ │
│ │ -XX:+UseG1GC │ │
│ │ ・リージョンベース │ │
│ │ ・停止時間目標を設定可能 │ │
│ │ ・大規模ヒープに対応 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ ZGC │ │
│ │ -XX:+UseZGC │ │
│ │ ・超低レイテンシ(1ms未満) │ │
│ │ ・TB級ヒープに対応 │ │
│ │ ・コンカレント(ほぼ全フェーズ) │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Shenandoah │ │
│ │ -XX:+UseShenandoahGC │ │
│ │ ・超低レイテンシ │ │
│ │ ・コンカレントコンパクション │ │
│ │ ・Red Hat開発 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

G1 GC(デフォルト)

リージョンベースのアーキテクチャ

┌─────────────────────────────────────────────────────────────────────┐
│ G1 GC のヒープ構造 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 従来のGC │
│ ┌──────────────────────────┐┌────────────────────────────────────┐ │
│ │ Young Generation ││ Old Generation │ │
│ └──────────────────────────┘└────────────────────────────────────┘ │
│ (連続した領域) │
│ │
│ G1 GC(リージョン分割) │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │ E │ S │ O │ O │ E │ H │ H │ O │ E │ S │ O │ O │ │ E │ O │ O │ │
│ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ │
│ │
│ E = Eden S = Survivor O = Old H = Humongous │
│ (空 = Free) │
│ │
│ ・リージョンサイズ: 1MB〜32MB(ヒープサイズにより自動決定) │
│ ・Humongous: リージョンの50%以上を占める大きなオブジェクト │
│ │
└─────────────────────────────────────────────────────────────────────┘

G1 GC のフェーズ

┌─────────────────────────────────────────────────────────────────────┐
│ G1 GC のサイクル │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Young-only フェーズ │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Young GC │────→│ Young GC │────→ ... │ │
│ │ │ (STW) │ │ (STW) │ │ │
│ │ └────────────┘ └────────────┘ │ │
│ │ │ │ │
│ │ Old の使用率が閾値を超えると │ │
│ │ ↓ │ │
│ │ ┌────────────────────────────────────────────────────────┐ │ │
│ │ │ Concurrent Mark (並行マーク) │ │ │
│ │ │ ・Initial Mark (STW) - Young GCと同時 │ │ │
│ │ │ ・Root Region Scan │ │ │
│ │ │ ・Concurrent Mark │ │ │
│ │ │ ・Remark (STW) │ │ │
│ │ │ ・Cleanup (STW) │ │ │
│ │ └────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Mixed Collection フェーズ │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │ Mixed GC │────→│ Mixed GC │────→ ... │ │
│ │ │ (Young+Old)│ │ (Young+Old)│ │ │
│ │ └────────────┘ └────────────┘ │ │
│ │ │ │
│ │ ゴミの多いリージョンを優先的に回収(Garbage-First) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

G1 GC の設定

java \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \ # 停止時間目標(ミリ秒)
-XX:G1HeapRegionSize=16m \ # リージョンサイズ
-XX:InitiatingHeapOccupancyPercent=45 \ # マーク開始閾値
-jar app.jar

ZGC(低レイテンシ)

特徴

項目ZGC
最大停止時間1ms未満(ヒープサイズに関係なく)
対応ヒープサイズ8MB〜16TB
コンカレント処理マーク、リロケート、参照処理
カラーポインタ64bitポインタにメタデータ埋め込み

ZGC のアーキテクチャ

┌─────────────────────────────────────────────────────────────────────┐
│ ZGC の特徴 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ カラーポインタ(Colored Pointers) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ 64bit ポインタ │ │
│ │ ┌───────────────┬────────────────────────────────────────────┐│ │
│ │ │ メタデータ(4) │ アドレス (44bit) ││ │
│ │ │ (Mark, Remap) │ ││ │
│ │ └───────────────┴────────────────────────────────────────────┘│ │
│ │ → オブジェクトの状態をポインタ自体に格納 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ロードバリア(Load Barriers) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ ・オブジェクト参照読み込み時にバリアを挿入 │ │
│ │ ・移動されたオブジェクトを透過的に追跡 │ │
│ │ ・アプリケーションと並行してGC実行可能 │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘

ZGC の設定

java \
-XX:+UseZGC \
-XX:+ZGenerational \ # 世代別ZGC(Java 21+推奨)
-Xmx16g \
-jar app.jar

Stop-The-World(STW)

STWとは

GC中にアプリケーションスレッドが一時停止する期間です。

┌─────────────────────────────────────────────────────────────────────┐
│ Stop-The-World │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 時間軸 ────────────────────────────────────────────────────────→ │
│ │
│ アプリ ████████████│ │████████████│ │██████████████ │
│ スレッド │ STW │ │ STW │ │
│ │ │ │ │ │
│ GC │██████│ │██████│ │
│ スレッド │ │ │ │ │
│ │
│ STW中: │
│ ・全アプリケーションスレッドが停止 │
│ ・リクエスト処理が中断 │
│ ・レイテンシスパイクの原因 │
│ │
└─────────────────────────────────────────────────────────────────────┘

Safepoint

STWはSafepointでのみ発生します。

// Safepointの例
while (true) {
// ループのバックエッジにSafepointあり
doWork();
}

// Safepointに到達しないコード(問題になることがある)
for (int i = 0; i < 1_000_000_000; i++) {
// counted loopにはSafepointがない
}

GCログ

有効化

# Java 9以降の統一ログ
java -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=10,filesize=100m -jar app.jar

# 詳細ログ
java -Xlog:gc*=debug:file=gc-debug.log -jar app.jar

ログの読み方

[2024-01-15T10:30:45.123+0900][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 24M->8M(256M) 5.234ms
│ │ │ │ │ │
│ │ │ │ │ └─ 停止時間
│ │ │ │ └─ 総ヒープ
│ │ │ └─ GC後
│ │ └─ GC前
│ └─ GCの原因
└─ GCの種類

GCログ分析ツール

ツール特徴
GCViewerGUIベース、グラフ表示
GCEasyWebベース、自動分析
HPjmeterHP製、詳細分析

GC選択の指針

┌─────────────────────────────────────────────────────────────────────┐
│ GC選択フローチャート │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ヒープサイズは? │
│ │ │
│ ├─ < 100MB → Serial GC │
│ │ │
│ ├─ 100MB〜4GB │
│ │ │ │
│ │ ├─ スループット重視 → Parallel GC │
│ │ └─ レイテンシ重視 → G1 GC │
│ │ │
│ └─ > 4GB │
│ │ │
│ ├─ 超低レイテンシ必須 → ZGC or Shenandoah │
│ │ (< 10ms の停止が必要) │
│ │ │
│ └─ 通常 → G1 GC │
│ │
│ ※ 迷ったら G1 GC(デフォルト) │
│ │
└─────────────────────────────────────────────────────────────────────┘

まとめ

項目ポイント
到達可能性GC Roots から辿れるオブジェクトは生存
世代別GCYoung(頻繁・高速)/ Old(低頻度・低速)
G1 GCリージョンベース、停止時間目標設定可能(デフォルト)
ZGC超低レイテンシ(1ms未満)、大規模ヒープ対応
STWGC中のアプリ停止、レイテンシに影響

次のステップ