Garbage Collection (GC): その概要、アルゴリズム、モバイル開発におけるガベージコレクション

著者: IT Sectr 公開日: 2026-03-29 読了時間: 10 分

ガベージコレクションによる自動メモリ管理は、ART仮想マシンに基づくAndroidプラットフォームの重要なメカニズムです。Google Android Documentation、2026によると、ガベージコレクタは開発者を手動のメモリ管理から解放し、参照がなくなったオブジェクトを自動的に削除します。GCがなければ、オブジェクトを割り当てるたびに明示的にfreeやdeleteを呼び出す必要があり、1秒間に数百万のオブジェクトを扱うJavaエコシステムでは物理的に不可能です。

重要ポイント

  • Garbage Collection — JavaとAndroidで未使用のオブジェクトを削除してメモリを解放する自動メカニズム
  • 基本アルゴリズム — Mark-and-Sweep、Copying Collection、Generational Collectionがコレクションの効率を決定する
  • ARTとDalvik — Android仮想マシンの2つの実装。ART(Android Runtime)はAndroid 5.0以降でDalvikに取って代わった
  • GCポーズ — コレクション中のアプリケーション実行の停止 — jankとパフォーマンス問題の主な原因
  • GC最適化 — 割り当ての削減、オブジェクトプールの使用、適切なコレクションタイプの選択によりコレクタの負荷を軽減

Garbage Collection (GC)とは?

Garbage Collection (GC) は、プログラムがもう使用しなくなったオブジェクトが占有するメモリを検出して解放する自動プロセスです。モバイル開発の文脈では、GCはART仮想マシンを介してAndroidプラットフォームで使用されるほか、標準のJava Virtual Machineでも使用されます。

手動メモリ管理言語(C、C++)とは異なり、プログラマーが明示的にfreeやdeleteを呼び出す必要があるのに対し、GCはオブジェクトのライフサイクル追跡を完全に引き受けます。開発者はnew演算子で新しいオブジェクトを作成し、コレクタはオブジェクトが到達不能になったタイミング — つまり、アクティブな参照がなくなったタイミングを判断します。

GC効率の主な指標は、ポーズ時間(pause time)とスループット(throughput)です。ポーズとは、コレクションのためにアプリケーションの実行が停止される期間です。モバイル環境では、8–16ミリ秒を超えるポーズはドロップフレーム(jank)として認識されます。

Google I/O 2019によると、Android 10のARTは一般的なGCポーズを2–4msに削減し、Android 4.4のDalvikと比較して70%減少しました。それでも、不適切なメモリ管理 — ループ内での頻繁なオブジェクト割り当て、不必要な一時インスタンスの作成 — はパフォーマンス問題の主な原因であり続けています。

ガベージコレクタの仕組み:基本アルゴリズム

JavaとAndroidのすべてのGC実装は、ポーズ時間とコレクションの完全性のバランスを達成するために組み合わされるいくつかの基本アルゴリズムに基づいています。これらのアルゴリズムを理解することは、GCフレンドリーなコードを書くために不可欠です。

Mark-and-Sweep

Mark-and-Sweep は最も単純なアルゴリズムで、2段階で動作します。Markフェーズでは、コレクタはルート参照(ルートセット)— ローカル変数、静的フィールド、スレッドスタック — からオブジェクトグラフをトラバースします。到達可能な各オブジェクトには生存フラグがマークされます。Sweepフェーズでは、コレクタはヒープ全体をスキャンし、マークされていないオブジェクトのメモリを解放します。

欠点はメモリの断片化です。Sweep後、空き領域と占有領域が交互に現れ、大きなオブジェクトの割り当てが困難になります。モバイルシナリオでは、ヒープが通常小さい(Androidでは64–512MB)ため、これは重要です。

Copying Collection

Copying Collection はヒープを2つの半領域(semi-space)に分割します。アクティブなオブジェクトは、隙間なく一方の半領域から他方にコンパクトにコピーされます。コピー後、古い半領域は完全に解放されたと宣言されます。このアルゴリズムは断片化を完全に排除しますが、2倍のメモリが必要です。

モバイル環境では、Copying Collectionは、統計的に早期に死亡する若いオブジェクト(弱い世代仮説)の高速クリーンアップのために、世代別コレクタによって使用されます。

Generational Collection

Generational Collection はヒープを世代に分割します:Young Generation(若いオブジェクト)とOld Generation(複数のコレクションを生き延びた古いオブジェクト)。若い世代のコレクション(Minor GC)は頻繁かつ迅速に実行されます。これは、ほとんどのオブジェクトが若いうちに死ぬためです。古い世代のコレクション(Major GCまたはFull GC)は頻度は低いですが、より長時間かかります。

java
// ジェネレーショナルGCのデモ:若いオブジェクトはすぐに死ぬ
void processItems(List<Item> items) {
    List<Result> results = new ArrayList<>();       // メソッド全体で生存
    for (Item item : items) {
        Result r = new Result(item.getValue());    // 即座に死ぬ
        if (r.isValid()) {
            process(r);                               // rがガベージになる
        }
    }
    saveResults(results);                             // resultsがOld Genに移行
}

この例では、Resultオブジェクトがループ内で作成され、すぐにガベージになります — これらはYoung GCの理想的な候補です。resultsオブジェクトはより長く生存し、Old Generationに移行します。世代の分離により、Minor GCは古いヒープに触れることなく、ミリ秒単位で若いオブジェクトをクリアできます。

AndroidにおけるGarbage Collection:ARTとDalvik

AndroidはDalvik VMからART(Android Runtime)へと進化し、GCの実装は両者の主要な違いの1つです。AndroidのGCアーキテクチャを理解することで、実際のデバイスでポーズを最小化するコードを書くことができます。

特性Dalvik(4.4まで)ART(5.0+)
GCタイプConcurrent Mark付きMark-and-SweepGenerational + Concurrent
典型的なポーズ10–30 ms2–4 ms
コンパクションなし(断片化が増加するのみ)あり(バックグラウンドで、アプリ停止なし)
AOTコンパイルJIT(Just-In-Time)AOT + JIT(ハイブリッド)

Dalvik GC

Dalvik は、Mark-and-Sweepとコンカレントフェーズの組み合わせを使用していました。Concurrent Markにより、オブジェクトグラフのトラバース中もアプリケーションは動作を継続できましたが、Sweepフェーズではすべてのスレッドを停止する必要がありました(Stop-The-World)。RAMが少ないデバイス(512MB — 1GB)では、ポーズは30msに達し、インターフェースに顕著な遅延を引き起こしました。さらに、Dalvikはヒープをコンパクト化しなかったため、長時間の使用後には断片化が進み、大きなオブジェクト(Bitmapなど)の割り当て時に、十分な総空きメモリがあるにもかかわらずOutOfMemoryErrorが発生する可能性がありました。

ART GC

ART(Android Runtime) は、コンカレントコンパクションを備えた世代別コレクタを導入しました。ヒープは3つの領域に分割されます:Young、Mature(Old Generationに類似)、Large Object Space(12KBを超えるオブジェクト用)。ほとんどの場合、Young Regionのコレクションはスレッドを停止せずに並行して行われます。Android 10+では、Concurrent Copyingが導入されました — コンパクションはStop-The-Worldなしでバックグラウンドスレッドで実行されます。

ARTのアーキテクチャのおかげで、典型的なGCポーズは2–4msに削減され、若いオブジェクトが大半を占めるシナリオでは0.5–1msにまで短縮されました。これにより、Androidデバイスはアクティブなメモリ操作中でも安定した60FPSを維持できるようになりました。

Javaのガベージコレクタの種類

Javaエコシステムには、それぞれ独自のパフォーマンスプロファイルを持つ複数のGC実装があります。Android開発では選択肢はARTに限定されますが、Java GCの知識はモバイルアプリケーションのサーバーサイドコードを作成する際や、Kotlin Multiplatformでの開発時に役立ちます。

Serial GC

Serial GC は、アプリケーションを完全に停止(Stop-The-World)させるシングルスレッドコレクタです。Mark、Sweep、Compactの各操作は1つのスレッドで実行されます。パフォーマンスは低く — モバイルサーバーには使用されません。最大100MBのヒープを持つ小さなアプリケーションにのみ適しています。

Parallel GC

Parallel GC(スループットコレクタとしても知られる)は、すべてのコレクションフェーズで複数のスレッドを使用します。最大スループット — アプリケーション実行時間に対するGC時間の最小化 — を目指しています。JVMで-XX:+UseParallelGCフラグを使用して有効化します。

G1 GC

G1(Garbage-First)GC はJava 9+のデフォルトコレクタです。ヒープは1–32MBのリージョンに分割されます。G1はポーズ時間を予測し、指定された制限(デフォルト200ms)内に収まるよう努めます。優先順位:最もガベージの多いリージョンから先にクリーンアップされます(名前の由来)。G1は、予測可能なポーズを持つ大規模ヒープのサーバー(4–64GB)に効果的です。

java
// ターゲットポーズ100msでG1 GCを有効化
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar

public class MemoryMonitor {
    private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB

    public void checkHeapUsage() {
        Runtime rt = Runtime.getRuntime();
        long used = rt.totalMemory() - rt.freeMemory();
        if (used > THRESHOLD) {
            System.out.println("Heap usage exceeded threshold: " + used);
            System.out.println("Consider reducing allocations");
        }
    }
}

Runtimeを介したヒープの監視により、メモリリークを早期に検出できます。安定した動作中にusedが最大ヒープの80%を超えた場合 — これはリークの可能性、またはアプリケーションによる過剰なメモリ消費のシグナルです。

GCの問題とモバイルアプリケーションにおけるメモリ最適化

現代のART GCでさえすべての問題を解決できるわけではありません — 不適切なメモリ使用は、jankとANR(Application Not Responding)の主な原因であり続けています。主要なシナリオと最適化方法を見ていきましょう。

GCポーズとJank

GCポーズ — コレクション中のアプリケーションスレッドの停止です。画面上では、2つのフレーム間の時間が16.6ms(60FPS)を超えたときのドロップフレームとして現れます。GCが30ms続くと、2つの代わりに1つのフレームしか描画されず — ユーザーはインターフェースのスタッタリングを目にします。

長時間ポーズの主な原因:Old Generationの多数の生存オブジェクト、ヒープの断片化、頻繁なFull GC。診断にはAndroid Studio Profilerとsystraceが使用されます。

GC負荷の削減

GCフレンドリーなコードの主なルール は、割り当てられるオブジェクトの数を最小限にすることです。新しいオブジェクトはそれぞれ、メモリ割り当てだけでなく、その後のコレクションも必要とします。GCが高速でも、毎秒1000の余分な割り当ては、コレクタにとって1000のチェックを生み出します。

  • ループ内でのオブジェクト作成を避ける — 作成をループの外に移動し、ローカル変数を再利用する
  • オブジェクトプールを使用する — Bitmap、byte[]などの重い構造には、Object PoolやRecyclerView.ViewHolderを使用する
  • プリミティブを優先する — Integerの代わりにint、Floatの代わりにfloatでオートボクシングを回避
  • SparseArrayを使用する — HashMap<Integer, V>の代わりに、Android SDKはプリミティブで動作するSparseArray、LongSparseArrayを提供
  • 連結の代わりにStringBuilder — 文字列の連結ごとに新しいStringオブジェクトが作成される

メモリリーク

メモリリーク は、オブジェクトがもはや必要なくなっても到達可能なままの場合に発生します。GCはそのようなオブジェクトを削除できず、メモリは徐々に枯渇します。典型的な原因:未登録のリスナー、Activityへの静的参照、外部コンテキストをキャプチャする匿名クラス、閉じられていないCursor/InputStream。

java
// メモリリーク:匿名クラスがActivityへの参照を保持
public void startTask() {
    new Thread(new Runnable() {                    // 暗黙的にthis(Activity)を保持
        @Override
        public void run() {
            // 長時間の操作...
            System.out.println("Done");
        }
    }).start();
}

// 修正:静的ネストクラス + WeakReference
private static class TaskRunnable implements Runnable {
    private WeakReference<Activity> activityRef;

    TaskRunnable(Activity activity) {
        this.activityRef = new WeakReference<>(activity);
    }

    @Override
    public void run() {
        Activity act = activityRef.get();
        if (act != null) {
            // Activityでの安全な作業
        }
    }
}

この例では、匿名のRunnableがActivityへの暗黙的な参照をキャプチャしています。スレッドが生きている限り — ユーザーが画面を閉じても、ActivityはGCによってコレクションされません。WeakReference + 静的クラスによる修正はこのチェーンを断ち切り、Activityの解放を可能にします。

よくある質問

AndroidのGCはJavaのGCとどう違うのですか?

Android(ART)のGCは、メモリが限られたモバイルデバイス向けに最適化された、コンカレントコンパクションを備えた世代別コレクタです。Java GC(G1、ZGC)は、大きなヒープと予測可能なポーズを持つサーバーサイドコレクタです。ART GCはJVMフラグを使用しません — すべてのチューニングはOSレベルで自動的に行われます。

GCにおけるStop-The-Worldとは何ですか?

Stop-The-World とは、コレクタがオブジェクトグラフを安全にトラバースしたりメモリを解放したりするために、アプリケーションのすべてのスレッドを一時停止する瞬間です。STWが長いほど、jankが顕著になります。ARTは、その世代別アーキテクチャのおかげで、典型的なSTW時間を2–4msに短縮しました。

Androidでメモリリークを検出するには?

Android Studio Memory Profilerを使用します — ヒープの成長、割り当て数を表示し、Heap Dumpを取得できます。詳細な分析には、LeakCanaryを使用します — ライブラリがリークを自動的に検出し、GCコレクションを妨げる参照チェーンを表示します。

Full GCはいつ発生し、なぜ危険なのですか?

Full GC は、Old Generationを含むすべてのヒープ世代の完全なコレクションです。モバイルアプリケーションでは、Full GCは50–200ms続く可能性があり、顕著なjankやANRを引き起こします。主な原因:ヒープの断片化、メモリリーク、Old Generationのしきい値超過。

Kotlinはメモリリークの回避にどのように役立ちますか?

Kotlinは、構造化された並行性を備えたコルーチンを提供します — スコープのキャンセルにより、すべての子コルーチンが自動的にキャンセルされ、リークが防止されます。Kotlinには、遅延初期化のためのlazyデリゲートや、一時オブジェクトの数を減らすスコープ関数もあります。

まとめ

  • Garbage Collection — 到達不能なオブジェクトを削除する自動メモリ管理、Android Runtimeの基盤
  • Mark-and-Sweep — 2フェーズコレクションの基本アルゴリズム、ヒープの断片化に悩まされる
  • Copying Collection — 生存オブジェクトをコンパクトな半領域にコピーして断片化を排除
  • Generational GC — ヒープを世代(Young/Old)に分割し、短命な若いオブジェクトのコレクションを高速化
  • AndroidのART — コンカレントコンパクションと2–4msのポーズを備えた世代別コレクタ、Android 5.0でDalvikを置き換え
  • GC最適化 — 割り当ての削減、オブジェクトプール、ラッパーの代わりにプリミティブ、HashMapの代わりにSparseArrayでコレクタの負荷を軽減
  • 診断 — Android Studio Profiler、systrace、LeakCanaryがメモリ問題を特定するための主要ツール

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください