yf0994 / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

FinalizableReferenceQueue keeps ClassLoaders around. #92

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
If the class loader which loaded the google-collections jar is let go
(consider redeploying a war to Apache Geronimo) the class loader will not
be able to be garbage collected.  This is caused by the thread started by
FinalizableReferenceQueue never ending.  In other words the garbage
collection (for the class loader) can only happen when the thread stops. 
The solution is to stop the thread when there are no more objects on the
queue.  This will happen eventually if the rest of the app behaves
correctly by not placing state outside of its class loader's environment. 
Attached is a version of the class which I have used to this effect.

This is also a problem in the OSGi space where every jar gets its own class
loader and they can be refreshed easily at runtime to do rolling
upgrades/bug fixes.

Original issue reported on code.google.com by nairb...@gmail.com on 28 Jul 2008 at 5:39

Attachments:

GoogleCodeExporter commented 9 years ago
The suggestion from techsy730 is very interesting. The idea would be that the 
Finalizer thread would die, not when the FRQ is no longer referenced as today, 
but when there are no longer any FinalizableWeakReferences (etc) that reference 
the FRQ. Perhaps with some amount of hysteresis to avoid continually creating 
and destroying Finalizer threads (we would get that more or less for free using 
ThreadPoolExecutor). Making this work in a race-proof way could be challenging 
but I think the end result would be substantially better than what we have 
today.

Original comment by emcma...@google.com on 10 Jul 2012 at 9:05

GoogleCodeExporter commented 9 years ago
Using a SoftReference instead of a WeakReference for the "handle" in the 
FinalizibleReferenceQueue may help with excessive amounts of stopping the 
thread just to have to create a new one again, but it carries its own risks. 
According to the API, a perfectly valid implementation of SoftReference would 
be to not clear it at all until the absolute latest the API mandates it be 
considered, in the final "Last ditch" GC before a OutOfMemoryError is thrown. 
Of course no sane JVM implementation will always wait that long (most use some 
sort of "timeout" after the last reference was used), but we have to consider 
that possibility.

Maybe a better idea would be an "expiring" reference. An object that holds a 
strong and a weak reference until time X passes, after which is nulls out the 
strong reference and only holds the weak reference. In fact, that would be a 
pretty nifty wrapper for existing references. (Contemplates making a new 
feature suggestion)

As for the thread safety, I did it by just having a private static lock in the 
class holding the static WeakReference, and all fetches and initilizations of 
that WeakReference were synchronized on that. For each of my 
FinalizibleReferences, I made the field holding the strong reference volatile. 
I'm pretty reasonably sure this covers all angles.

Now, since in my workaround, I am creating whole new 
FinalizibleReferenceQueue's, I don't have to care about the thread safety of 
spawning the new thread using the fancy ClassLoader and reflection magic that 
is done, as it's a whole new instance. If this sort of reinitializable weak 
handle approach was brought into FinalizibleReferenceQueue, then the fancy Java 
magic would have to be made thread safe. But I agree that it would be worth it.

(Actually, a reinitializable weak reference and a reinitializable soft 
reference are also cool idea, maybe another for the feature suggestions)

Original comment by techsy730 on 10 Jul 2012 at 9:29

GoogleCodeExporter commented 9 years ago
I've added a close() method for now (and made FRQ implement Closeable). Even if 
we later add a mechanism that closes automatically when there are no 
FinalizableReferences left, the close() method will still be useful for when it 
is not practical to dispose of all the referenced objects explicitly.

Original comment by emcma...@google.com on 1 Aug 2012 at 3:15

GoogleCodeExporter commented 9 years ago
So does this mean that every library that uses Guava's 
FinalizableReferenceQueue has to expose a close() method itself because the 
library may not know that it is used inside a web application and have no 
notion about redeployments?
For example would the close() method help to fix Google Guice's issue #288 
(http://code.google.com/p/google-guice/issues/detail?id=288) which is pretty 
much the same as this one?

Original comment by jens.neh...@gmail.com on 1 Aug 2012 at 3:52

GoogleCodeExporter commented 9 years ago
To address a WAR memory leak caused by this bug, just add a 
ServletContextListener that uses reflection to invoke the close() method (if 
found by reflection).

See Spring's IntrospectorCleanupListener for example of this idea applied to 
the Introspector class: 
http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework
/web/util/IntrospectorCleanupListener.html

Original comment by archie.c...@gmail.com on 1 Aug 2012 at 3:57

GoogleCodeExporter commented 9 years ago
Wrt. Guice issue 288 the only fix required is to update it to use Guava 10.0 or 
later, which is when the weak/soft maps created by MapMaker stopped using the 
FinalizableReferenceQueue. The change to use a more recent build of Guava has 
already been committed to Guice trunk (it's currently using 11.0.1) and will be 
in the next release.

Original comment by mccu...@gmail.com on 1 Aug 2012 at 4:18

GoogleCodeExporter commented 9 years ago
You don't need to use reflection to invoke the close() method even if you are 
concerned about compatibility with earlier Guava versions. Just write
  if (frq instanceof Closeable) {
    ((Closeable) frq).close();
  }

Original comment by emcma...@google.com on 1 Aug 2012 at 4:41

GoogleCodeExporter commented 9 years ago
issue 1505 may or may not be relevant here

Original comment by jerith666 on 16 Aug 2013 at 5:57

GoogleCodeExporter commented 9 years ago
Marking this as Fixed since it is possible to avoid the problem by calling 
close() at the appropriate time. We do not currently plan to implement a 
mechanism that closes automatically when there are no FinalizableReferences 
left.

Original comment by emcma...@google.com on 20 Nov 2013 at 11:23

GoogleCodeExporter commented 9 years ago
For Guava 13.0.1 (which is a requirement for BoneCP) I had to modify the fix 
from comment #15:

http://pastie.org/8976099
Insteas of thread.getClass().getName(); I had to use thread.getName(); 

Also important is, that this must not be proxied by aspectj. The method was not 
invoke here if this was the case.

Original comment by Karsten....@googlemail.com on 28 Mar 2014 at 2:33

GoogleCodeExporter commented 9 years ago
I suggest opening a new bug report that references this one because most 
committers will ignore comments on closed issues.

Original comment by cow...@bbs.darktech.org on 28 Mar 2014 at 2:37

GoogleCodeExporter commented 9 years ago
I believe comment #110 is a note about a workaround that is needed if you have 
to use an old version of Guava, correct? Obviously we can't modify old Guava 
versions retroactively. If there is something that needs to be done to the 
current version then by all means open a new issue.

Original comment by emcma...@google.com on 28 Mar 2014 at 4:43

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:16

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:10