2017-03-15 78 views
1

我遇到幻像参考时出现问题,当参照物是类内部的字段时。当类对象被设置为空,字段不会自动通过GC一个类的内部字段不由GC通过幻像参考收集

Controller.java

public class Controller { 
     public static void main(String[] args) throws InterruptedException 
    { 
     Collector test = new Collector(); 
     test.startThread(); 

     Reffered strong = new Reffered(); 
     strong.register(); 
     strong = null; //It doesn't work 
     //strong.next =null; //It works 
     test.collect(); 
     Collector.m_stopped = true; 
     System.out.println("Done"); 
    } 
} 

Collector.java收集:我有,其登记一个目的是参考队列并打印出来,当它收集器被收集。

import java.lang.ref.PhantomReference; 
import java.lang.ref.Reference; 
import java.lang.ref.ReferenceQueue; 
import java.util.HashMap; 
import java.util.Map; 

public class Collector { 
     private static Thread m_collector; 
     public static boolean m_stopped = false; 
     private static final ReferenceQueue refque = new ReferenceQueue(); 
     Map<Reference,String> cleanUpMap = new HashMap<Reference,String>(); 
     PhantomReference<Reffered> pref; 


     public void startThread() { 
      m_collector = new Thread() { 
       public void run() { 
        while (!m_stopped) { 
         try { 
           Reference ref = refque.remove(1000); 
           System.out.println(" Timeout "); 
                if (null != ref) { 
           System.out.println(" ref not null "); 

          } 
         } catch (Exception ex) { 
          break; 
         } 
        } 
       } 
      }; 
      m_collector.setDaemon(true); 
      m_collector.start(); 
     } 

     public void register(Test obj) { 
      System.out.println("Creating phantom references"); 


       //Referred strong = new Referred(); 
       pref = new PhantomReference(obj, refque); 
       cleanUpMap.put(pref, "Free up resources"); 

     } 

     public static void collect() throws InterruptedException { 
     System.out.println("GC called"); 
     System.gc(); 
     System.out.println("Sleeping"); 
     Thread.sleep(5000); 
    } 
} 

Reffered.java

public class Reffered { 

     int i; 
     public Collector test; 
     public Test next; 

     Reffered() { 
      test= new Collector(); 
      next = new Test(); 

     } 
     void register() { 
      test.register(next); 
     } 
    } 

测试是一个空类。当Reffered对象设置为null时,我可以看到Refferred类中的“next”字段未被收集。换句话说,当“强”被设置为空时,“下一个”不被收集。我认为GC将自动收集“下一个”,因为当“strong”设置为空时“next”不再被引用。但是,当“strong.next”设置为空时,我们认为收集“下一个”。为什么当strong被设置为null时不会自动收集“next”?

回答

1

你有一个非常混乱的代码结构。

在你的代码的开头,你有那么你创建的Collector实例的语句

Collector test = new Collector(); 
test.startThread(); 

后台线程将有一个参考。该线程甚至没有触及该引用,但由于它是一个匿名内部类,它将持有对其外部实例的引用。

在​​您有初始化在构造函数new Collector(),换句话说,你创建的Collector另一个实例Collector类型的字段。这是您调用register的实例。

因此,所有的文物由register创建,在pref举行的PhantomReferencecleanUpMap举行的HashMap,这也给PhantomReference参考,只能通过Collector由​​引用的实例引用。如果​​实例变得无法访问,则所有这些工件都变得无法访问,并且不会在队列中注册任何内容。

这是回忆java.lang.ref package documentation的地方:

注册参考对象和它的队列之间的关系是片面的。也就是说,一个队列不跟踪注册的引用。如果注册的引用本身变得无法访问,那么它永远不会入队。程序使用引用对象负责确保只要程序对其对象感兴趣,对象就可以保持可达状态。

有一些方法可以说明您的程序的问题。
而不是做要么,strong = null;strong.next = null;的,你可以这样做既

strong.next = null; 
strong = null; 

这里,这不要紧,next已经归零了,这个变量是无法访问的,无论如何,一旦strong = null已执行。之后,只能通过​​实例访问的PhantomReference本身已无法访问,并且不会显示“ref not null”消息。

或者,你可能是代码的一部分改为

strong.next = null; 
strong.test = null; 

这也将使得PhantomReference可达,从而从不排队。

但如果你将其更改为

Object o = strong.test; 
strong = null; 

消息“裁判不空”将被打印成o持有间接引用PhantomReference。必须强调的是,这不是保证的行为,允许Java消除未使用的局部变量的影响。但是,对于当前的HotSpot实施来说,它足够重现以证明这一点。


底线是,Test实例总是按预期方式收集。只是在某些情况下,收集的数据比您知道的还多,包括PhantomReference本身,所以没有发生任何通知。

作为最后一条评论,您在两个线程之间共享的变量如public static boolean m_stopped必须声明为volatile以确保线程将注意到由另一个线程所做的修改。它恰好在这里没有工作,因为JVM的优化器对于如此短的运行程序并没有做很多工作,而像x68这样的体系结构同步缓存。但它不可靠。

+0

谢谢,@holger。 – GoT