我倾向于幻影引用,我很困惑当引用物被垃圾收集时,幻像引用是如何排队的。
这是我的代码
Object s = new Object();
ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
PhantomReference<Object> ref = new PhantomReference<Object>(s, queue);
s = null;
System.gc();
TimeUnit.SECONDS.sleep(1);
System.out.println(queue.poll());
正如预期的那样,queue.poll将返回幻像引用:ref。
但是如果我对代码做了一点改动:删除局部变量“ref”,queue.poll将返回null。
所以我可以推断,当JVM尝试垃圾收集一个对象时,它会检查所有引用以查看是否有任何可以到达该对象的引用。
这应该是一个非常缓慢的进展。?
我设计了一个程序:使用幻像引用来跟踪资源泄漏。分配资源时,幻像引用是新的并绑定到资源。然后我发现:必须存储所有幻像引用,否则幻像引用不会入队。
我检查了网络代码,发现netty存储了所有幻像引用:io.netty.util.ResourceLeakDetector #allallaks。
答案 0 :(得分:1)
垃圾收集器像任何其他对象一样处理引用对象。来自the documentation:
如果注册的引用本身无法访问,则它永远不会被排队。
理论上,是的,使用引用对象会导致垃圾收集效率低下,因此我不会为每个应用程序对象创建引用对象。但在实践中,参考对象集往往非常小:它们旨在跟踪资源,如数据库连接,而不是任意对象。
答案 1 :(得分:1)
如the specification所述,“如果注册的引用本身无法访问,则它将永远不会入队”。但是,这不应该让人感到意外,因为没有定义特殊可达性状态的可达参考对象,就没有特殊的可达性状态。规范明确指出的一点是,只要引用对象尚未入队,就没有从队列到引用对象的引用。
所以我可以推断,当JVM尝试垃圾收集一个对象时,它会检查所有引用以查看是否有任何可以到达该对象的引用。
除了JVM从不尝试收集单个对象之外,您刚刚描述了垃圾收集的全部内容,遍历所有引用以找出哪些对象仍然可以访问。
这应该是一个非常缓慢的进展。?
如果垃圾收集器为每个对象再次执行此操作,那将非常慢。但是垃圾收集器会对所有对象进行遍历。遍历期间未遇到的对象本身就是垃圾,因此这些无法访问的对象不需要任何特殊处理,包括放弃的引用对象。
但是,垃圾收集当然会带来开销,这就是为什么System.gc()
不应该被调用的原因,除非在示例程序中进行调试。