WeakReference と ReferenceQueue

今日は新しい技を会得したぞ!

Java のオブジェクトが GC で回収されたことを検知したい場合、いくつかの方法があります。

対象のオブジェクトに finalize() を実装しておく、というのは最も簡単な方法ですが、副作用があるし、何より自分で実装したクラスじゃなければ使用できません。

そういう場合、以下のように WeakReference を使用します。

obj = new Object();
WeakReference ref = new WeakReference(obj);
while (ref.get() != null) {
    Thread.sleep(100);
}

ただ、この方法は若干の問題があります。ひとつは、ref.get() で実体を参照してしまっていること、もうひとつは Android のようなレジスタマシンだとメソッドの戻り値がレジスタを占有してしまい、参照が発生してしまうことです。スタックマシンだと即スタックが書き換えられるので参照を保持し続ける、ということはありません。

この問題を解決するのが ReferenceQueue です。

obj = new Object();
ReferenceQueue queue = new ReferenceQueue();
new WeakReference(obj, queue);
do {
    Reference ref = queue.poll();
    if (ref == null) {
        break;
    }
    ref.enqueue();
    Thread.sleep(100);
} while (true);

WeakReference のコンストラクタで渡した ReferenceQueue は、WeakReference の実体が GC で回収されると、キューからいなくなります。まだ参照が残っている場合は poll() でキューから取り出して、そのリファレンスに対して再び enqueue() を行うことでキューに入れ、これを参照がなくなるまで繰り返します。

コードは複雑になるのですが、参照の実体を触らなくても良いので、VM の実装に依存しないコードが書けるようになります。