2017-03-15 67 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