11

I want to properly close Closeable object when it's no longer referenced by other threads.

I wrote some small test, but after object is enqueued the get method return null, i.e. the poll method returns proper Object which has no referent.

  public static void main(String[] args)
  {
   ReferenceQueue<Closeable> reaped = new ReferenceQueue<Closeable>();
   Closeable s = <SOME CLOSEABLE IMPL>;
   WeakReference<Closeable> ws = new WeakReference<Closeable>(s, reaped);
   s = null;

   System.gc();
   Closeable ro = (Closeable)reaped.poll().get();
   ro.close();
  }

Thanks in advance. Any help will be appreciated.

koders
  • 5,654
  • 1
  • 25
  • 20
yan
  • 149
  • 3
  • 11

2 Answers2

8

First, if it is only about closing, use PhantomReference. Next, from the reference queue, poll() does not guarantee that you will get the reference back. and you will never get the actual object (referent) back.

If you want to make sure your Closeables are closed you have to keep track of them yourself lets say in a Map<Reference<?>, Closeable>. Then when you poll() your reference queue, you will eventually get the ref then you have to use it to get the Closeable from the map.

   class MyThing {
      Closeable c;
   }

   Map<Reference<MyThing>, Closeable> m = new HashMap();
   ReferenceQueue<MyThing> reaped = new ReferenceQueue<MyThing>();

   MyThing mt = new MyThing();
   mt.c = new MyClosable();

   Reference<MyThing> pref = new PhantomReference<MyThing>(mt, reaped);
   m.put(pref, mt.c);

   mt = null;


   System.gc();
   Reference<MyThing> rf = reaped.poll();
   while (rf != null) {
     m.get(rf).close(); 
     rf = reaped.poll();
   }

Note If you don't have a real reason to do this or if you do not understand what are you really doing, DO NOT do this kind of thing.

You can close your files in finally and BTW if it is about files, sockets, etc, they are closed for you (they already implement finalize()

czerwin
  • 972
  • 8
  • 10
Op De Cirkel
  • 28,647
  • 6
  • 40
  • 53
  • 1
    Hi thanks for the such quick response, it's about IndexSearcher's of Lucene. – yan Jun 04 '12 at 09:44
  • I just tried it, after the System.gc(); the ReferenceQueue size is zero. – yan Jun 04 '12 at 09:57
  • @yan there is no guarantee that your objects will get GCes after `System.gc()` not even when you exit the JVM. You do not use finalization for your business logic. It is there only for special purposes like caching and (eventually) preventing resource leaks. You can not let say `commit()` or `rollback()` DB transactions in finalizer – Op De Cirkel Jun 04 '12 at 09:58
  • Thank you, you'are right, that's why i used only Closeable interface to allow close resources only. And i don't mind oblects will not GC until Memory runs out. – yan Jun 04 '12 at 13:19
  • 1
    `IndexSearcher` is a very heavyweight object and I can't see how could this approach possibly be sane. `IndexSearcher` is usually kept around as as singleton for the entire duration of the application. Modern Lucene versions should use `SearcherManager` or `NRTManager` to get the full maintenance cycle out of the box. – Marko Topolnik Nov 16 '12 at 09:22
0

Here I want to mention a couple of points

  1. The get() method is not meant to be used for finalization task The purpose of get() method is to have access to the referent object because we use WeakReference object to have reference to an object which doesn't have a direct reference (i.e not strongly reachable). The referent object could be garbage collected any time. The get() method is expected to return the referent object until it is not garbage collected, and return null when it is garbage collected. This make sense because the referent object is not available post garbage collection. The get() method is not meant for doing finalization task like closing Closable object.

  2. The approach to do finalization task is not straight forward. The approach to do finalization task is to retrieve the WeakReference object after it's enqueued in ReferenceQueue post garbage collection. and call it's clear() method. You can not have the referent object to do finalization. Because at this point it is believed that the referent object has been garbage collected. The approach is to have a custom implementation of the Reference object that will extend WeakReference. You can override the clear() method to place any cleanup logic. Still you cant have any reference to the referrent object in the implementaion as it will block the referrent object from being garbage collected. If you have to cleanup some states , for example the referent object has some closeble object or connection etc, they need to be saved as the instance variable under the implementaion and in the clear method they can be finalized. As the clear() method gets called the finaliation logic will be executed. Thus the Reference object will facilitate the finalization.

  3. Still the above approach is not useful here as even the custom implementation of WeakReference object can not keep reference to the referent object itself as it will block the referent object to be garbage collected. You are facing such difficulty because using WeakReference for Closeble and expecting to close it as a post-mortem activity is not right usecase. Because closing a Closeble is not qualified to be a post mortem activity. Post mortem activity is what is done after an object is garbage collected i.e it doesn't have it states at all. Closing a Closeble object is a pre-mortem activity i.e expected to be done in the finally block or as it is in the thread where it is created. There is no point of delaying it post the garbage collection of the Closeble. ReferenceQueue is designed to be used in post-mortem activity , not for pre-mortem activity.

Skgland
  • 862
  • 10
  • 24