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 にも頼らずに、AutoCloseable と try-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」をご参照ください。