ReferenceQueue に enqueue されるタイミング

先ほど嵌ったのでエントリ書きます。


Jiemamy のコードを弄って JUnit テストを動かしていたときに気づいたんですが、あるオブジェクトに対して弱参照を行い、参照が切れて GC したはずなのに、ReferenceQueue にそのオブジェクトに対する弱参照インスタンスが enqueue されていないという不可思議な現象が起きました。
もちろん、その参照オブジェクトを弱参照インスタンスから get() しようと思っても、GC されてるので結果は null です。
意味わからんので、ネットで調べてもそれらしい情報にひっかからなかったため、仕方ないので自分で試すことに。
試験的に書いたコードは下記の通り。

public class WeakReferenceTest {

  public static void main(String[] args) {
    MyObject myObj = new MyObject();
    ReferenceQueue<MyObject> queue = new ReferenceQueue<MyObject>();
    Reference<MyObject> ref = new WeakReference<MyObject>(myObj, queue);

    // 参照を切って GC
    myObj = null;
    System.gc();

    // ReferenceQueue のチェックと、弱参照インスタンスから get() してみる。
    System.out.println(queue.poll());
    System.out.println(ref.get());
  }

  private static class MyObject {
  }

}

上記の状態だと、myObj への弱参照を作った上で、生成した myObj への参照を切り、GC するので queue に ref が enqueue される上に、ref.get() の結果は null になるはずです。
で、動かしてみたら。。

null
null

え、どっちも null !?
ref.get() の結果は当然なので、納得できます。しかし参照が切れたことを VM が理解してるはずなのになんで queue に enqueue されてないの!?
いろいろ試しましたが、昨晩はよくわからず不貞寝。
で、先ほど起床後いろいろ試したら、ようやく期待する動きになりました。
それが下記のコード。

public class WeakReferenceTest {

  public static void main(String[] args) throws InterruptedException {
    MyObject myObj = new MyObject();
    ReferenceQueue<MyObject> queue = new ReferenceQueue<MyObject>();
    Reference<MyObject> ref = new WeakReference<MyObject>(myObj, queue);

    // 参照を切って GC
    myObj = null;
    System.gc();

    Thread.sleep(1000L); // このコードを追加

    // ReferenceQueue のチェックと、弱参照インスタンスから get() してみる。
    System.out.println(queue.poll());
    System.out.println(ref.get());
  }

  private static class MyObject {
  }

}

結果は下記の通り。

java.lang.ref.WeakReference@1a758cb
null

1秒待っただけで enqueue されてるとは。。。
あれなんすかね、enqueue されるタイミングとかがあるんですかねぇ。