ReferenceQueue 的含义

gst*_*low 1 java garbage-collection weak-references phantom-reference

我试着理解课堂 ReferenceQueue

它是可选的构造函数参数

SoftReference
Run Code Online (Sandbox Code Playgroud)

WeakReference
Run Code Online (Sandbox Code Playgroud)

它也是 的强制性参数PhantomReference

根据我读过的信息,我可以写一些论文

a) 对于 PhantomReference 方法 get 总是返回 null

b) 对于 Phantom 引用:
1. gc 检测到该对象可以从内存中删除
2. 对放入 ReferenceQueue 的对象的引用,
当我们调用 clear 或 link to reference from queue 时无法访问并且 gc 看到 3. finalize 方法调用
4.
为弱/软引用释放内存
1. gc 检测到该对象可以从内存中删除
2. 终结方法调用
3. 释放内存
4. 对放入队列的对象的引用

  1. 我什么时候可以将第二个参数传递给XXXReference构造函数?
  2. 我可以获得哪些帮助?
  3. 为什么PhantomReference没有没有构造函数ReferenceQueue
  4. 获取方法的 ReferenceQuee 总是返回 null 的原因是什么?

Hol*_*ger 5

也许,以下程序会有所帮助:

public class SimpleGCExample {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Object> queue=new ReferenceQueue<>();
        SimpleGCExample e = new SimpleGCExample();
        Reference<Object> pRef=new PhantomReference<>(e, queue),
                          wRef=new WeakReference<>(e, queue);
        e = null;
        for(int count=0, collected=0; collected<2; ) {
            Reference ref=queue.remove(100);
            if(ref==null) {
                System.gc();
                count++;
            }
            else {
                collected++;
                System.out.println((ref==wRef? "weak": "phantom")
                                  +" reference enqueued after "+count+" gc polls");
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalizing the object in "+Thread.currentThread());
        Thread.sleep(100);
        System.out.println("done finalizing.");
    }
}
Run Code Online (Sandbox Code Playgroud)

在我的系统上,它打印

public class SimpleGCExample {
    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<Object> queue=new ReferenceQueue<>();
        SimpleGCExample e = new SimpleGCExample();
        Reference<Object> pRef=new PhantomReference<>(e, queue),
                          wRef=new WeakReference<>(e, queue);
        e = null;
        for(int count=0, collected=0; collected<2; ) {
            Reference ref=queue.remove(100);
            if(ref==null) {
                System.gc();
                count++;
            }
            else {
                collected++;
                System.out.println((ref==wRef? "weak": "phantom")
                                  +" reference enqueued after "+count+" gc polls");
            }
        }
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalizing the object in "+Thread.currentThread());
        Thread.sleep(100);
        System.out.println("done finalizing.");
    }
}
Run Code Online (Sandbox Code Playgroud)

或者

weak reference enqueued after 1 gc polls
finalizing the object in Thread[Finalizer,8,system]
done finalizing.
phantom reference enqueued after 2 gc polls
Run Code Online (Sandbox Code Playgroud)

由于多线程,前两条消息的顺序有时会有所不同。有时,据报告,幻象引用在 3 次轮询后进入队列,表明它花费了超过指定的 100 毫秒。

关键是

  • 软引用和弱引用在开始终结之前或就被清除并入队
  • 幻象引用终结入队,假设对象没有泄漏finalize方法,否则在对象再次变得不可达后入队
  • 一个(非平凡的)finalize()方法的存在导致需要至少一个额外的垃圾收集周期来检测对象是不可达或幻象再次可达

由于超过 99% 的所有对象不需要终结,因此强烈建议所有 JVM 供应商检测何时finalize()未被覆盖或“微不足道”,即空方法或唯一super.finalize()调用。在这些情况下,应省略最终确定步骤。通过删除finalize()上面示例中的方法,您可以轻松检查此优化是否发生在您的 JVM中。然后它打印

finalizing the object in Thread[Finalizer,8,system]
weak reference enqueued after 1 gc polls
done finalizing.
phantom reference enqueued after 2 gc polls
Run Code Online (Sandbox Code Playgroud)

由于两者同时入队并以任意顺序检索,因此两条消息的顺序可能不同。但它们总是在一个 gc 周期后排入队列。

值得注意的是,虚拟引用不会自动清除,这意味着它需要另一个垃圾回收周期,直到对象的内存真正可以被重用,所以上面的例子需要至少三个周期使用非平凡finalize()方法,两个不使用. Java 9 将改变这一点,自动清除幻像引用,因此在上面的示例中,它将需要两个循环完成,一个没有完成,直到内存真正可以被回收。好吧,准确地说,在这个简单的例子中,对象的内存永远不会被回收,因为程序在这之前终止了。


上面的代码还演示了参考 API 的预期用例之一。我们可以使用它来检测对象的可达性何时在我们完全控制的代码中发生变化,例如在main方法中使用循环。相反,finalize()可以在任意时间由不同的、未指定的线程调用。该示例还表明您可以在不需要该get()方法的情况下从参考对象中提取信息。

实际应用程序经常使用引用类的子类来向它们添加更多信息。这就是WeakHashMap.Entry扩展WeakReference并记住哈希码和值所发生的事情。清理可以在普通地图操作中完成,不需要任何线程同步。这对于finalize()方法来说是不可能的,除了 map 实现无法将finalize()方法推送到键的类中的事实。

这意味着“比最终确定更灵活”一词。

WeakHashMap演示了get()方法是有用的。只要尚未收集键,它就会被报告为在映射中,并且可以在迭代所有键或条目时检索。

PhantomReference.get()方法已被覆盖以始终返回,null以防止应用程序可以恢复入队引用的所指对象。这是“幻像引用不会自动清除”规则的直接后果。这个规则本身是有问题的,它的初衷是在黑暗中。尽管该规则即将在下一个 Java 版本中更改,但恐怕get()将继续始终返回null到向后兼容。