Overhead of a new thread was: Benefits of activeReferenceQueue

Peter Levart peter.levart at gmail.com
Tue Jul 29 08:27:50 UTC 2014


On 07/29/2014 10:18 AM, Jaroslav Tulach wrote:
>
> Thanks Peter,
>
> I had a feeling that two threads might address the issue and your work 
> indicates they really can. I am sort of worried about the overhead of 
> a single thread (~1MB?), but certainly using two threads is better 
> than having our current reflection which works on JDK7, JDK8, but is 
> broken on JDK9. I see you in the list of approved OCAs: 
> http://www.oracle.com/technetwork/community/oca-486395.html#l
>
> and as such I think I can copy your sample and use it to improve 
> NetBeans. You will have been able to consider yourself a NetBeans 
> contributor when NetBeans 8.0.1 is out. Thanks.
>

Go ahead. One thing you could do is to create threads with a minimal 
stack size (at least the 2nd thread that doesn't execute user code) like 
it was done recently in java.lang.Process where there's one thread per 
process that just waits for process to exit...

Regards, Peter

> As for destiny of
>
> https://bugs.openjdk.java.net/browse/JDK-8051843
>
> - it depends whether we consider the one more signal thread overhead 
> important or not. I do. I would still like JDK9 to allow me to write 
> activeReferenceQueue with a single cleanup thread.
>
> Or even more effective attempt: Maybe JDK could expose such queue by 
> itself. In such case we don't need much visible APIs and we can even 
> save one thread - as the call back the Runnables in existing Finalizer 
> one. Towards lightweight (and correct) finalization!
>
> -jt
>
> Dne Po 28. července 2014 19:04:25, Peter Levart napsal(a):
>
> > Hello Jaroslav,
>
> >
>
> > Regardless of where it is implemented (in JDK or in NetBeans) it would
>
> > be impossible to create something that doesn't do polling in a single
>
> > thread and yet wait for two ReferenceQueues at the same time - one is
>
> > the queue of Runnable references which you run() and the other is the
>
> > queue of WeakReference(s) to the before-mentioned queue(s) (the ACTIVE
>
> > in ActiveQueue). Each of these two queues has it's own 'lock' and there
>
> > is no way to wait() for two locks at the same time in a single thread.
>
> > But you could do this in two threads. I created an alternative
>
> > implementation using two threads:
>
> >
>
> > 
> http://cr.openjdk.java.net/~plevart/misc/nb.ActiveQueue2/ActiveQueue2.java
>
> >
>
> > I deliberately commented-out the setDaemon(true) calls of both threads
>
> > so it can be seen that JVM exits when both are finished in the little
>
> > test at the end.
>
> >
>
> > It's easy to adapt this to use polling instead of 2nd thread. Just
>
> > specify a time-out in a call to CleanupHandler.remove(timeout) and scrap
>
> > the code for 2nd thread. The remaining thread will eventually find out
>
> > that ReferenceQueue has been collected and will exit.
>
> >
>
> > Regards, Peter
>
> >
>
> > On 07/28/2014 03: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/Ut
>
> > > ilities.html#activeReferenceQueue() [2]
>
> > > https://netbeans.org/bugzilla/show_bug.cgi?id=206621
>




More information about the core-libs-dev mailing list