2013-08-05 7 views
6

मैं उन तंत्रों को लागू करने की कोशिश कर रहा हूं जो कैश की गई फ़ाइलों को हटाते हैं, जब ऑब्जेक्ट्स उन्हें मरते हैं, और किसी ऑब्जेक्ट के कचरे के संग्रह पर अधिसूचित होने के लिए PhantomReference का उपयोग करने का निर्णय लिया जाता है। समस्या यह है कि मैं ReferenceQueue के अजीब व्यवहार का अनुभव करता रहता हूं। जब मैं अपने कोड में कुछ बदलता हूं तो अचानक यह वस्तुएं नहीं लाता है। बुला gc कई बार आदिमेरी वस्तुएं क्यों मर जाएंगी?

I'm born. 
I'm born. 
I'm born. 
I'm born. 
I'm born. 
I'm born. 
I'm born. 
I'm born. 
I'm born. 
I'm born. 

जरूरत नहीं कहने के लिए, sleep समय बदल रहा है, तो मैं परीक्षण के लिए इस उदाहरण करने की कोशिश की, और एक ही समस्या में पड़ गए:

public class DeathNotificationObject { 
    private static ReferenceQueue<DeathNotificationObject> 
      refQueue = new ReferenceQueue<DeathNotificationObject>(); 

    static { 
     Thread deathThread = new Thread("Death notification") { 
      @Override 
      public void run() { 
       try { 
        while (true) { 
         refQueue.remove(); 
         System.out.println("I'm dying!"); 
        } 
       } catch (Throwable t) { 
        t.printStackTrace(); 
       } 
      } 
     }; 
     deathThread.setDaemon(true); 
     deathThread.start(); 
    } 

    public DeathNotificationObject() { 
     System.out.println("I'm born."); 
     new PhantomReference<DeathNotificationObject>(this, refQueue); 
    } 

    public static void main(String[] args) { 
     for (int i = 0 ; i < 10 ; i++) { 
      new DeathNotificationObject();     
     } 
     try { 
      System.gc();  
      Thread.sleep(3000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

उत्पादन होता है काम नहीं किया

अद्यतन

के रूप में सुझाव दिया है, मैं अपने संदर्भ है, जो समस्या हल की Reference.enqueue() कहा जाता है।

अजीब बात यह है कि मेरे पास कुछ कोड है जो पूरी तरह से काम करता है (बस इसका परीक्षण करता है), हालांकि यह कभी भी enqueue पर कॉल नहीं करता है। क्या यह संभव है कि Reference को Map में किसी भी तरह से जादुई रूप से संदर्भ को घेर लिया जाए?

public class ElementCachedImage { 
    private static Map<PhantomReference<ElementCachedImage>, File> 
      refMap = new HashMap<PhantomReference<ElementCachedImage>, File>(); 
    private static ReferenceQueue<ElementCachedImage> 
      refQue = new ReferenceQueue<ElementCachedImage>(); 

    static { 
     Thread cleanUpThread = new Thread("Image Temporary Files cleanup") { 
      @Override 
      public void run() { 
       try { 
        while (true) { 
         Reference<? extends ElementCachedImage> phanRef = 
           refQue.remove(); 
         File f = refMap.remove(phanRef); 
         Calendar c = Calendar.getInstance(); 
         c.setTimeInMillis(f.lastModified()); 
         _log.debug("Deleting unused file: " + f + " created at " + c.getTime()); 
         f.delete(); 
        } 
       } catch (Throwable t) { 
        _log.error(t); 
       } 
      } 
     }; 
     cleanUpThread.setDaemon(true); 
     cleanUpThread.start(); 
    } 

    ImageWrapper img = null; 

    private static Logger _log = Logger.getLogger(ElementCachedImage.class); 

    public boolean copyToFile(File dest) { 
     try { 
      FileUtils.copyFile(img.getFile(), dest); 
     } catch (IOException e) { 
      _log.error(e); 
      return false; 
     } 
     return true; 
    } 

    public ElementCachedImage(BufferedImage bi) { 
     if (bi == null) throw new NullPointerException(); 
     img = new ImageWrapper(bi); 
     PhantomReference<ElementCachedImage> pref = 
       new PhantomReference<ElementCachedImage>(this, refQue); 
     refMap.put(pref, img.getFile()); 

     new Thread("Save image to file") { 
      @Override 
      public void run() { 
       synchronized(ElementCachedImage.this) { 
        if (img != null) { 
         img.saveToFile(); 
         img.getFile().deleteOnExit(); 
        } 
       } 
      } 
     }.start(); 
    } 
} 

कुछ फ़िल्टर्ड उत्पादन:

2013-08-05 22: 35: फाइल करने के लिए 01,932 डीबग सहेजें छवि: <> \ AppData \ Local \ अस्थायी \ tmp7..0.PNG

2013-08-05 22: 35: 03,379 DEBUG अप्रयुक्त फ़ाइल को हटा रहा है: <> \ AppData \ Local \ Temp \ tmp7..0.PNG सोमवार को बनाया गया 05 अगस्त 22:35:02 आईडीटी 2013

+0

आप वास्तव में कुछ भी नहीं लगाते हैं। –

+0

तो मुझे 'enqueue()' कॉल करना चाहिए ... इसे मिला, धन्यवाद! – Elist

+0

मेरा उत्तर, आखिरी पंक्ति देखें। –

उत्तर

6

उत्तर है, कि आपके उदाहरण में PhantomReference स्वयं पहुंच योग्य नहीं है और इसलिए कचरा एकत्रित से पहले निर्दिष्ट वस्तु स्वयं कचरा एकत्रित है। तो उस समय ऑब्जेक्ट जीसीड है, वहां Reference नहीं है और जीसी को यह नहीं पता है कि इसे कहीं और कुछ लगाया जाना चाहिए।

निश्चित रूप से

यह (अपने नए कोड में गहरी करने के लिए देख के बिना) :-)

यह भी बताते हैं सिर-से-शीर्ष दौड़ किसी तरह का कारण है कि कुछ ही पहुंचा जा सकता संग्रह में संदर्भ डाल बनाता है उदाहरण के काम करते हैं।

बस संदर्भ के लिए (पन इरादा) यहां आपके पहले उदाहरण का एक संशोधित संस्करण है जो काम करता है (मेरी मशीन पर :-) मैंने अभी सभी संदर्भों को एक सेट जोड़ा है।

import java.lang.ref.PhantomReference; 
import java.lang.ref.Reference; 
import java.lang.ref.ReferenceQueue; 
import java.util.HashSet; 
import java.util.Set; 

public class DeathNotificationObject { 
    private static ReferenceQueue<DeathNotificationObject> refQueue = new ReferenceQueue<DeathNotificationObject>(); 
    private static Set<Reference<DeathNotificationObject>> refs = new HashSet<>(); 

    static { 
     Thread deathThread = new Thread("Death notification") { 
      @Override 
      public void run() { 
       try { 
        while (true) { 
         Reference<? extends DeathNotificationObject> ref = refQueue.remove(); 
         refs.remove(ref); 
         System.out.println("I'm dying!"); 
        } 
       } catch (Throwable t) { 
        t.printStackTrace(); 
       } 
      } 
     }; 
     deathThread.setDaemon(true); 
     deathThread.start(); 
    } 

    public DeathNotificationObject() { 
     System.out.println("I'm born."); 
     PhantomReference<DeathNotificationObject> ref = new PhantomReference<DeathNotificationObject>(this, refQueue); 
     refs.add(ref); 
    } 

    public static void main(String[] args) { 
     for (int i = 0 ; i < 10 ; i++) { 
      new DeathNotificationObject();     
     } 
     try { 
      System.gc();  
      Thread.sleep(3000); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

अद्यतन

हाथ से enqueue कॉलिंग अपने उदाहरण में संभव है, लेकिन वास्तविक कोड में नहीं है। यह सादा गलत परिणाम देता है।मुझे निर्माता में enqueue बुला और का उपयोग करके दिखा एक और main:

public DeathNotificationObject() { 
    System.out.println("I'm born."); 
    PhantomReference<DeathNotificationObject> ref = new PhantomReference<DeathNotificationObject>(this, refQueue); 
    ref.enqueue(); 
} 

public static void main(String[] args) throws InterruptedException { 

    for (int i = 0 ; i < 5 ; i++) { 
     DeathNotificationObject item = new DeathNotificationObject(); 

     System.out.println("working with item "+item); 
     Thread.sleep(1000); 
     System.out.println("stopped working with item "+item); 
     // simulate release item 
     item = null; 
    } 

    try { 
     System.gc();  
     Thread.sleep(3000); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

उत्पादन इस तरह होगा:

I'm born. 
I'm dying! 
working with item [email protected] 
stopped working with item [email protected] 

जिसका मतलब है कि जो कुछ भी आप कर किया जाएगा संदर्भ कतार के साथ करना चाहता था जब आइटम अभी भी जीवित है।

+0

मैं इसे बाद में बहुत सावधानी से पढ़ने जा रहा हूं। –

+0

आप सही हैं, 'enqueue' यहां कोई अच्छा नहीं है! धन्यवाद – Elist

संबंधित मुद्दे