【プログラミングしてみよう52】JavaのWeakReferenceとSoftReference入門

Java

前回はStackとQueueを使ったデータ構造の使い方を学びました。

今回は「WeakReference(弱参照)」と「SoftReference(ソフト参照)」について解説します。 Javaのガベージコレクション(GC)の仕組みと合わせて理解することで、メモリを効率よく扱うプログラムが書けるようになります。

参照の種類とGCの基本

Javaはガベージコレクション(GC)という仕組みで、不要になったオブジェクトのメモリを自動的に解放します。「不要」とは「どこからも参照されていない」状態のことです。

Javaの参照には強さのレベルがあります。

強参照(Strong Reference)  // 通常の参照。GCで回収されない
ソフト参照(Soft Reference) // メモリ不足のときだけGCで回収される
弱参照(Weak Reference)    // 次のGC実行時に回収される
ファントム参照(Phantom Reference) // GC後の後処理に使う(上級者向け)

通常のコードで使う参照はすべて「強参照」です。

String str = "hello"; // 強参照: strが参照している間はGCされない
str = null;           // 参照を切るとGC対象になる

WeakReference と SoftReference を使うことで、GCの挙動を制御したキャッシュの実装などができるようになります。

サンプルコード(WeakReferenceの動作)

ファイル作成

「Java40」フォルダに WeakRefTest.java という名前でファイルを作成します

コード入力

以下のコードを入力します。

import java.lang.ref.WeakReference;

public class WeakRefTest {
    public static void main(String[] args) throws InterruptedException {
        // 強参照
        String strong = new String("強参照のオブジェクト");

        // 弱参照
        WeakReference<String> weak = new WeakReference<>(new String("弱参照のオブジェクト"));

        System.out.println("GC前:");
        System.out.println("強参照: " + strong);
        System.out.println("弱参照: " + weak.get());

        // GCを実行する(ヒント: 必ず実行されるとは限らない)
        System.gc();
        Thread.sleep(100);

        System.out.println("\nGC後:");
        System.out.println("強参照: " + strong);
        System.out.println("弱参照: " + weak.get()); // nullになる可能性がある

        // 強参照を切ってからGC
        strong = null;
        System.gc();
        Thread.sleep(100);

        System.out.println("\n強参照をnullにしてGC後:");
        System.out.println("弱参照: " + weak.get());
    }
}

コンパイル

javac WeakRefTest.java

実行

java WeakRefTest

実行結果は次のようになります(GCのタイミングにより異なる場合があります)。

GC前:
強参照: 強参照のオブジェクト
弱参照: 弱参照のオブジェクト

GC後:
強参照: 強参照のオブジェクト
弱参照: null

強参照をnullにしてGC後:
弱参照: null

サンプルコード(WeakHashMapの活用)

WeakHashMap はキーを弱参照で保持するMapです。キーが他から参照されなくなると、GC時にそのエントリが自動的に削除されます。

ファイル作成

「Java40」フォルダに WeakHashMapTest.java という名前でファイルを作成します

コード入力

以下のコードを入力します。

import java.util.WeakHashMap;
import java.util.Map;

public class WeakHashMapTest {
    public static void main(String[] args) throws InterruptedException {
        Map<String, String> weakMap = new WeakHashMap<>();

        // キーを強参照で保持している間はエントリが残る
        String key1 = new String("キー1");
        String key2 = new String("キー2");

        weakMap.put(key1, "値1");
        weakMap.put(key2, "値2");

        System.out.println("GC前のサイズ: " + weakMap.size());
        System.out.println("key1の値: " + weakMap.get(key1));

        // key1の強参照を切る
        key1 = null;

        System.gc();
        Thread.sleep(100);

        System.out.println("\nkey1をnullにしてGC後:");
        System.out.println("サイズ: " + weakMap.size());
        System.out.println("key2の値: " + weakMap.get(key2));

        // key2の強参照も切る
        key2 = null;

        System.gc();
        Thread.sleep(100);

        System.out.println("\nkey2もnullにしてGC後:");
        System.out.println("サイズ: " + weakMap.size());
    }
}

コンパイル

javac WeakHashMapTest.java

実行

java WeakHashMapTest

実行結果は次のようになります。

GC前のサイズ: 2
key1の値: 値1

key1をnullにしてGC後:
サイズ: 1
key2の値: 値2

key2もnullにしてGC後:
サイズ: 0

SoftReferenceとの違い

SoftReference は WeakReference よりも長く生き残ります。

import java.lang.ref.SoftReference;

SoftReference<String> soft = new SoftReference<>(new String("ソフト参照"));
WeakReference  // 次のGC実行時に回収される(すぐ消える)
SoftReference  // メモリが不足したときだけ回収される(しばらく残る)

SoftReference はメモリに余裕があるうちはオブジェクトを保持し続けるため、画像や計算結果のキャッシュとして活用できます。メモリが逼迫したときだけGCで回収されるため、OutOfMemoryErrorを防ぎながらキャッシュを使えます。

// SoftReferenceを使ったシンプルなキャッシュの例
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;

Map<String, SoftReference<byte[]>> cache = new HashMap<>();

// キャッシュに保存
cache.put("image1", new SoftReference<>(loadImage("image1.png")));

// キャッシュから取得
SoftReference<byte[]> ref = cache.get("image1");
byte[] image = (ref != null) ? ref.get() : null;
if (image == null) {
    // GCで回収されていたら再読み込み
    image = loadImage("image1.png");
    cache.put("image1", new SoftReference<>(image));
}

コードの解説

new WeakReference<>(obj) で弱参照を作成します。weak.get() で参照先のオブジェクトを取得できますが、GCで回収済みの場合は null が返ります。そのため weak.get() の戻り値は必ずnullチェックが必要です。

System.gc() はGCの実行をJVMに「ヒント」として伝えるメソッドですが、必ず実行されるとは保証されていません。実際の本番コードでは System.gc() を明示的に呼ぶことはほとんどなく、JVMが自動的に適切なタイミングでGCを実行します。

WeakHashMap はキーを弱参照で保持するため、キーへの強参照がなくなるとそのエントリ自体がGC時に自動削除されます。セッション管理やキャッシュなど、「キーが使われている間だけ値を保持したい」場面に向いています。

まとめ

通常の参照(強参照)はGCで回収されませんが、WeakReference は次のGC実行時に、SoftReference はメモリ不足時にそれぞれ回収されます。weak.get() や soft.get() で値を取り出す際は必ずnullチェックが必要です。WeakHashMap はキーが参照されなくなると自動的にエントリを削除するMapで、キャッシュやセッション管理に活用できます。これらはやや上級の内容ですが、メモリ効率の高いプログラムを書く際の重要な道具です。

2026/07/31(金) ぱんまる

コメント

タイトルとURLをコピーしました