I am trying to implement a mechanism that deletes cached files when the objects that hold them die, and decided to use PhantomReference to get a notification about the garbage collection of the object. The problem is that I continue to experience the strange behavior of ReferenceQueue . When I change something in my code, it suddenly does not retrieve objects anymore. So I tried to make this example for testing and ran into the same problem:
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(); } } }
Output:
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.
Needless to say, changing the sleep time, calling gc several times, etc. did not work.
UPDATE
As I said, I called Reference.enqueue() my link, which solved the problem.
It is strange that I have code that works fine (just tested it), although it never calls enqueue . Is it possible that placing the Reference in a Map somehow magically placed in a link?
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(); } }
Filtered Output:
2013-08-05 22: 35: 01,932 DEBUG Save the image to a file: <> \ AppData \ Local \ Temp \ tmp7..0.PNG
2013-08-05 22: 35: 03,379 DEBUG Delete an unused file: <> \ AppData \ Local \ Temp \ tmp7..0.PNG created in Mon Aug 05 22:35:02 IDT 2013