Java : PhantomReference (ファントム参照) - API使用例

PhantomReference (Java SE 18 & JDK 18) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。


概要

ファントム参照オブジェクトです。ファントム参照オブジェクトがキューに入れられるのは、キューに入れておかないとそれらのリファレントが再生される可能性があるとコレクタが判断したときです。 幻の参照は、死後のクリーンアップ・アクションをスケジュールするために最も頻繁に使用されます。

クラス構成

PhantomReferenceクラスは、対象となるオブジェクトのファントム参照を作ります。

PhantomReferenceは、通常 ReferenceQueue(キュー) と一緒に使います。
対象のオブジェクトが finalize されたあとに、ファントム参照がキューに追加されます。

そして、キューからファントム参照を取り出して最後の後処理を行う…といった使い方が主になりそうです。

class Foo {
    @SuppressWarnings("removal")
    @Override
    protected void finalize() {
        System.out.println("Finalize!");
    }
}

class FooRef extends PhantomReference<Foo> {
    FooRef(Foo referent, ReferenceQueue<? super Foo> q) {
        super(referent, q);
    }

    void clean() {
        System.out.println("Clean!");
    }
}

final var queue = new ReferenceQueue<Foo>();
final var executorService = Executors.newSingleThreadExecutor();

try {
    executorService.submit(() -> {
        try {
            var foo = new Foo();
            final var ref = new FooRef(foo, queue);

            // get は常に null を返します。
            System.out.println("ref.get : " + ref.get());
            System.out.println("ref.referesTo : " + ref.refersTo(foo));

            System.out.println("queue.poll : " + queue.poll());

            // 変数 foo による強参照をクリアします。
            foo = null;

            System.out.println("-- gc --");
            System.gc();

            TimeUnit.SECONDS.sleep(3);
            System.out.println("-- sleep --");

            System.out.println("-- gc --");
            System.gc();

            TimeUnit.SECONDS.sleep(3);
            System.out.println("-- sleep --");

        } catch (InterruptedException e) {
            System.out.println(e);
        }
    });

    final var ref = queue.remove();
    System.out.println("queue.remove : " + ref.getClass().getSimpleName());

    if (ref instanceof FooRef fooRef) {
        fooRef.clean();
    }

} finally {
    executorService.shutdown();
}

final var ret = executorService.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("term : " + ret);

// 結果
// ↓
//ref.get : null
//ref.referesTo : true
//queue.poll : null
//-- gc --
//Finalize!
//-- sleep --
//-- gc --
//queue.remove : FooRef
//Clean!
//-- sleep --
//term : true

個人的には、使いどころが難しいと思う Reference です。
PhantomReference を直接使わずに、Cleaner クラスを使ったほうが便利かもしれません。

さらにいうと、Cleaner にも頼らずに、AutoCloseabletry-with-resources文 で即座に後処理するのがおすすめです。

補足

  • 本記事のコード例では、GCを実行するために System.gc メソッドを使っています。
  • しかし、一般的なJavaプログラムでは、System.gc を直接呼び出すことはあまりおすすめしません。
    Javaシステムが自分自身の最適なタイミングでGCを実行してくれるためです。

コンストラクタ

PhantomReference (T referent, ReferenceQueue<? super T> q)

指定されたオブジェクトを参照し、指定されたキューに登録されている新しいファントム参照を作成します。

var sb = new StringBuilder("abcd");

final var queue = new ReferenceQueue<StringBuilder>();
final var ref = new PhantomReference<>(sb, queue);

System.out.println(ref.get()); // null
System.out.println(ref.refersTo(sb)); // true

System.out.println(queue.poll()); // null

sb = null;
System.gc();

System.out.println(queue.remove()); // java.lang.ref.PhantomReference@6cb107fd

メソッド

T get ()

参照オブジェクトのリファレントを返します。

final var sb = new StringBuilder("abcd");

final var queue = new ReferenceQueue<StringBuilder>();
final var ref = new PhantomReference<>(sb, queue);

System.out.println(ref.get()); // null
System.out.println(ref.refersTo(sb)); // true

Referenceで宣言されたメソッド

clear, clone, enqueue, isEnqueued, reachabilityFence, refersTo

Java API 使用例 : Reference」をご参照ください。


関連記事

ページの先頭へ