Benefits of activeReferenceQueue was: ReferenceQueue.remove to allow GC of the queue itself
David Holmes
david.holmes at oracle.com
Tue Jul 29 02:16:33 UTC 2014
Hi Jaroslav,
So ... activeReferenceQueue is a reference queue that embodies a thread
that does the polling and implements a psuedo-finalization mechanism.
This works fine in the normal case where the lifetime of the queue is
the lifetime of the "application". In the WAR case (and I don't know the
details of WAR deployment) each time it is deployed in the same VM we
get a new activeReferenceQueue and a new thread.
The basic issue is that the thread has a strong reference to the queue
and has no idea when it should exit and so the thread and queue remain
forever even if there is no user code with a reference to the queue -
does that sum it up?
Can the thread not hold a weakreference to the queue and poll using
remove(timeout) and then terminate when the queue reference is gone?
Thanks,
David
On 28/07/2014 11:06 PM, Jaroslav Tulach wrote:
> Hello David,
>
> thanks for being patient with me. I'll do my best to describe the
> original context.
>
> Dne Po 28. července 2014 21:07:45, David Holmes napsal(a):
>
> > I read the issue and still did not understand the nature of the problem.
>
> > The netbeans bugs also did not shed any light on things for me. What is
>
> > the functionality of the activeReferenceQueue
>
> The functionality of the active reference queue is described in NetBeans
> APIs[1]. I think the best way to describe it in context of existing JDK
> APIs, is to call it "lightweight finalizer without classical finalizer
> problems". To quote the Javadoc:
>
> ---
>
> If you have a reference that needs cleanup, make it implement Runnable
> and register it with the queue:
>
> class MyReference extends WeakReference implements Runnable {
>
> private final OtherInfo dataToCleanUp;
>
> public MyReference(Thing ref, OtherInfo data) {
>
> super(ref, Utilities.activeReferenceQueue());
>
> dataToCleanUp = data;
>
> }
>
> public void run() {
>
> dataToCleanUp.releaseOrWhateverYouNeed();
>
> }
>
> }
>
> When the ref object is garbage collected, your run method will be
> invoked by calling ((Runnable) reference).run()
>
> --
>
> The benefit taken from "finalizer" is that one does not need to start
> own thread. The difference to "finalizer" is that the object is already
> gone, e.g. no chance to re-activate it again.
>
> We introduced the activeReferenceQueue API when we realized that many
> modules over the code base start their own thread and try to do the
> classical poll() cleanup. Once upon a time we used to have more than
> twenty threads like this, and as overhead of a thread is not low, we
> improved the NetBeans memory consumption quite a lot by introducing the
> activeReferenceQueue.
>
> > and what it is that there
>
> > are problems with?
>
> None in case of NetBeans. Once the activeReferenceQueue initializes
> itself and its thread, it runs up until the termination of the system
> and works great.
>
> However NetBeans APIs can be used outside of NetBeans runtime container
> and, when used in a WAR file, people started to get problems during
> re-deploys.
>
>> Once we got a bug report[2] that it behaves poorly
>
>> when used inside of a WAR file. Whenever the WAR was redeployed, the number
>
>> of cleanup threads increased by one, which also caused major memory leaks.
>
> Those problems could be fixed by using active polling as I wrote in
> today's morning email:
>
> > class Impl extends ReferenceQueue {}
>
> > Reference<Impl> ref = new WeakReference<Impl>(new Impl());
>
> >
>
> > while (true) {
>
> >
>
> > Impl impl = ref.get();
>
> > if (impl == null) {
>
> >
>
> > // no other Reference objects using the Impl queue.
>
> > // exit this cleaner thread
>
> > return;
>
> >
>
> > }
>
> > Reference<?> ref = impl.remove(15000);
>
> > if (ref == null) {
>
> >
>
> > impl = null; // don't hold strong ref to Impl queue
>
> > System.gc(); // XXX: is anyone else holding reference to Impl queue?
>
> > continue;
>
> >
>
> > }
>
> > // do something with ref
>
> >
>
> > }
>
> >
>
> > this could work, althrough the problem is the XXX part.
>
> >
>
> > I need to release my own pointer to the Impl queue, tell the system
> to try
>
> > to garbage collect it. If it has not been removed, grap new strong
> pointer
>
> > to the Impl queue and wait again. I am not aware of any other way to ask
>
> > for GC than System.gc, and having System.gc being called every 15s will
>
> > likely decrease VM performance a bit.
>
> >
>
> > The proper solution (no reflection, no repeated polling) would in fact be
>
> > simple: Being able to call:
>
> >
>
> > impl.remove();
>
> >
>
> > without anyone having strong reference to impl - e.g. without impl
> being on
>
> > stack during the remove call.
>
> I don't know what else to add. So I wait for further question.
>
> -jt
>
> [1]
> http://bits.netbeans.org/dev/javadoc/org-openide-util/org/openide/util/Utilities.html#activeReferenceQueue()
>
> [2] https://netbeans.org/bugzilla/show_bug.cgi?id=206621
>
More information about the core-libs-dev
mailing list