Why won't my objects die?

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

+6
source share
1 answer

The answer is that in your example, the PhantomReference itself PhantomReference not available and therefore the garbage collected before , the selected object itself is garbage collected. Thus, while the GCed object is no longer a Reference , and the GC does not know that it needs to put something somewhere.

This, of course, is some kind of head-to-head race :-)

It also explains (without looking deep into your new code) why including a link in some accessible collection makes this example work.

For reference only (for pun intended) there is a modified version of your first example here that works (on my machine :-) I just added a set containing all the links.

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

Update

Calling enqueue manually is possible in your example, but not in real code. this gives the wrong result. Let me show by calling enqueue in the constructor and using another 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(); } } 

The output will be like this:

 I'm born. I'm dying! working with item DeathNotificationObject@6908b095 stopped working with item DeathNotificationObject@6908b095 

This means that everything you wanted to do with the reference queue would be done when the element is still alive.

+6
source

Source: https://habr.com/ru/post/951056/


All Articles