2013-08-05 6 views
6

キャッシュされたファイルを保持するオブジェクトが消滅したときに、キャッシュされたファイルを削除するメカニズムを実装しようとしていて、オブジェクトのガベージコレクションで通知を受け取るためにPhantomReferenceを使用することに決めました。問題は、私がReferenceQueueの奇妙な動作を経験し続けていることです。コード内で何かを変更すると、突然オブジェクトをフェッチしません。だから私はテストのために、この例を作ってみました、と同じ問題に遭遇した:なぜ私のオブジェクトが死ぬことはありませんか?

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

出力は次のとおりです。sleep時間を変更し、言うこと

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. 

言うまでもなく、gc複数回などを呼び出しますうまくいかなかった。

UPDATE

示唆したように、私は問題を解決した、私の参照のReference.enqueue()と呼ばれます。

奇妙なことに、完全に(ちょうどそれをテストした)コードがありますが、それは決してenqueueを呼び出しません。 ReferenceMapに入れると、どうやら魔法のようにリファレンスをエンキューできますか?

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年8月5日22:35:01932 DEBUG保存画像ファイルへ:<> \のAppData \ローカル\ Tempにを\ tmp7..0.PNG

2013-08-05 22:35:03,379 DEBUG未使用ファイルを削除する:<> \ AppData \ Local \ Temp \ tmp7..0.PNG Mon Aug 05 22:35:02 IDT 2013

+0

実際には何もエンキューしません。 –

+0

だから私は 'enqueue()'を呼び出すべきです...それを得ました、ありがとう! – Elist

+0

私の答え、最後の行を参照してください。 –

答えて

6

答えは、あなたの例ではPhantomReferenceそれ自体に到達できないため、ガベージコレクトの前にはが参照されたオブジェクト自体がガベージコレクションされます。オブジェクトがGCされた時点では、Referenceはなく、GCはどこかにエンキューする必要があることを知りません。当然の

これは、いくつかの到達可能なコレクションに参照を置くことは例の作業を行い、なぜ(あなたの新しいコードの奥深くまで見ずに)

これも説明:-)頭に頭レースのいくつかの種類です。

私のマシン上で:-)動作する最初の例の修正バージョンです。ここでは、すべての参照を保持しているセットを追加しました。

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

あなたは正しいです、 'エンキュー'はここでは良いものではありません!ありがとう – Elist

関連する問題