2013-08-05 35 views
6

Tôi đang cố gắng triển khai một cơ chế xóa các tệp đã lưu trong bộ nhớ cache khi các đối tượng giữ chúng chết và quyết định sử dụng PhantomReference để nhận thông báo về việc thu thập rác của một đối tượng. Vấn đề là tôi tiếp tục gặp phải hành vi kỳ lạ của ReferenceQueue. Khi tôi thay đổi một cái gì đó trong mã của tôi nó đột nhiên không lấy các đối tượng nữa. Vì vậy, tôi đã cố gắng để làm ví dụ này để thử nghiệm, và chạy vào cùng một vấn đề:Tại sao đối tượng của tôi không chết?

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

Đầu ra là:

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. 

Không cần phải nói, việc thay đổi thời gian sleep, gọi gc nhiều lần, vv không hoạt động.

CẬP NHẬT

Như đã đề cập, tôi gọi Reference.enqueue() tham chiếu của tôi, trong đó đã giải quyết được vấn đề.

Điều kỳ lạ, là tôi có một số mã hoạt động hoàn hảo (chỉ cần thử nghiệm nó), mặc dù nó không bao giờ gọi enqueue. Có thể đặt Reference vào một số Map bằng cách nào đó kỳ diệu đưa ra tham chiếu?

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

Một số sản lượng lọc:

2013/08/05 22: 35: 01.932 DEBUG Lưu hình ảnh sang file: <> \ AppData \ Local \ Temp \ tmp7..0.PNG

2013-08-05 22: 35: 03,379 DEBUG Xóa tệp không sử dụng: <> \ AppData \ Local \ Temp \ tmp7..0.PNG được tạo tại Mon Aug 05 22:35:02 IDT 2013

+0

Bạn không thực sự enqueue bất cứ điều gì. –

+0

Vì vậy, tôi nên gọi là 'enqueue() '... đã nhận nó, cảm ơn! – Elist

+0

Xem câu trả lời của tôi, dòng cuối cùng. –

Trả lời

6

Câu trả lời là, trong ví dụ của bạn, bản thân số PhantomReference không thể truy cập được và do đó thu thập rác thải trước đối tượng được giới thiệu là rác được thu thập. Vì vậy, tại thời điểm đối tượng được GCed không có thêm Reference và GC không biết rằng nó nên enqueue một cái gì đó ở đâu đó.

Điều này tất nhiên là một số loại chủng tộc đầu-to-đầu :-)

này cũng giải thích (không tìm cách để sâu vào mã mới của bạn) tại sao đặt tham chiếu vào một số bộ sưu tập có thể truy cập làm cho công việc ví dụ.

Chỉ để tham khảo (chơi chữ dự định) ở đây là phiên bản sửa đổi của ví dụ đầu tiên hoạt động (trên máy của tôi :-) Tôi vừa thêm một tập hợp giữ tất cả các tham chiếu.

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

Cập nhật

Calling enqueue bằng tay là có thể trong ví dụ của bạn, nhưng không có trong mã thực. nó cho kết quả sai đơn giản.Hãy để tôi chỉ bằng cách gọi enqueue trong các nhà xây dựng và sử dụng một 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(); 
    } 
} 

Đầu ra sẽ là như thế này:

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

Có nghĩa là bất cứ điều gì bạn muốn làm với hàng đợi tham khảo sẽ được thực hiện khi vật phẩm vẫn còn sống.

+0

Tôi sẽ đọc kỹ điều này sau. –

+0

bạn nói đúng, 'enqueue' không có gì tốt ở đây! Cảm ơn – Elist

Các vấn đề liên quan