WAR == single classloader was: Benefits of activeReferenceQueue
Jaroslav Tulach
jaroslav.tulach at oracle.com
Tue Jul 29 08:05:27 UTC 2014
Hello David.
Dne Út 29. července 2014 12:16:33, David Holmes napsal(a):
> 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.
WAR contains a set of JARs that form a "classpath". Each time WAR is re-
deployed, a new classloader with a new "classpath" is created. I don't know
anything certain about WARs either, but this is my naive vision of what
happens.
> 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?
Plus, because there is a single classloader which loads all the classes from a
WAR, by keeping the activerReferenceQueue thread alive and holding reference
to the queue, we hold reference to its class and the classloader and thus all
classes referenced by it.
And this memory leak accumulates with every re-deploy.
> Can the thread not hold a weakreference to the queue and poll using
> remove(timeout) and then terminate when the queue reference is gone?
The NetBeans team does not consider active polling good practice. But of
course, you are right. Such solution has been analyzed in earlier. See below.
-jt
> > 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/Uti
> > lities.html#activeReferenceQueue()
> >
> > [2] https://netbeans.org/bugzilla/show_bug.cgi?id=206621
More information about the core-libs-dev
mailing list