2013-08-05 8 views
6

Önbelleğe alınmış nesneleri silerken, onları öldüren nesneler silindiğinde ve bir nesnenin çöp toplamasında bildirim almak için PhantomReference s kullanmaya karar veren bir mekanizmayı uygulamaya çalışıyorum. Sorun şu ki, ReferenceQueue'un tuhaf davranışlarını devam ettirmekteyim. Kodumda bir şeyi değiştirdiğimde, artık nesneleri bir anda almaz. gc birden çok kez vb çağırarak,Nesnelerim neden ölmüyor?

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. 

Söylemeye gerek sleep zaman değişen: Ben test için bu örneği yapmaya çalıştı ve aynı sorun koştu:

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(); 
     } 
    } 
} 

çıkışı işe yaramadı önerildiği gibi

GÜNCELLEME

ben de sorunu çözüldü benim referans ait Reference.enqueue() aradı.

Garip olan şey, hiçbir zaman mükemmel çalışıyor (sadece test ettim), hiç bir zaman enqueue numaralı telefonu aramamasına rağmen. Reference'un bir Map içine yerleştirilmesi, bir şekilde referansı sihirli bir şekilde sıralamak mümkün mü?

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(); 
    } 
} 

Bazı süzülmüş çıkışı:

2013/08/05 22: 35: 01.932 DEBUG Resmi kaydet dosyaya: <> \ AppData \ Local \ Temp tmp7..0.PNG

\

2013-08-05 22: 35: 03,379 DEBUG Kullanılmayan dosyayı silme: <> \ AppData \ Local \ Temp \ tmp7..0.PNG, 09.09.2012 tarihinde saat 22: 35'te alındı. IDT 2013

+0

Aslında hiçbir şeyi düzenlemezsiniz. –

+0

Bu yüzden 'enqueue() 'yi çağırmalıyım ... anladım, teşekkürler! – Elist

+0

Cevabım, son satırımı görün. –

cevap

6

Yanıt, örneğinizde PhantomReference'un kendisine ulaşılamıyor ve dolayısıyla çöp toplanıyor 'dan önce, atıf yapılan nesnenin kendisi toplanır. Nesne GCed olduğu zaman, daha fazla Reference yok ve GC, bir yerde bir şeyleri yerleştirmesi gerektiğini bilmiyor. Tabii

Bu, bazı ulaşılabilir koleksiyonuna atıf koyarak örnek iş yapar neden (yeni koduna derin bakmadan)

de açıklar :-) kafa kafaya yarışın bazı türüdür.

Sadece referans için (pun amaçlı) burada çalıştığınız ilk örneğinizin değiştirilmiş bir sürümü var (makinemde :-) Tüm referansları tutan bir set ekledim.

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(); 
     } 
    } 
} 

Güncelleme

elle enqueue çekmek, örnekte mümkün değil gerçek kodu gönderiyor. yanlış yanlış sonuç verir.Beni yapıcısı içinde enqueue arayıp kullanarak gösterelim başka 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(); 
    } 
} 

çıkışı böyle olacak: Eğer bitmiş olacaktır referans kuyruğuna ile yapmak istediğini demektir

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

öğe hala hayatta olduğunda.

+0

Bunu çok dikkatli bir şekilde okuyacağım. –

+0

haklısın, 'enqueue burada hiç iyi değil! Teşekkürler – Elist