I have been working on a classloader leak in our application and finally got to a point where all references to the CL were gone. In my memory profiling tool (using YourKit and jmap/jhat), I can force a GC that sometimes will immediately get rid of my classloader, but then other times (depends on application usage but that's as specific as I can get) when I force the GC, the CL instance doesn't go away. I capture a memory snapshot and look at the results and it says that this object exists, but is unreachable.
Here's the wacky part... and I only discovered this by accident.
I can force full GC's all I want but the instance just doesn't go away. HOWEVER, after 10-20 minutes, if I do another full GC, it does get collected.
(During this time period the application is mostly inactive (not totally). My "extended" coffee break was the accident that led to this discovery.)
So my concern about a leak here is gone (hopefully), but the question is now more of trying to explain this behavior.
Anyone know what might cause the sun JVM to decide to not collect an unreachable classloader for 20 minutes?
Sun JVM version details:
java version "1.6.0_23"
Java(TM) SE Runtime Environment (build 1.6.0_23-b05)
Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode)
I think Jeremy Heiler is on the right track with it being a finalization issue. Basically, even if the GC knows an object is unreachable, it may still be an indefinite amount of time before the object gets actually collected, since it may be enqueued for finalization. The finalizer thread will have to get around to finalizing the object (which could take a while, depending on how many other objects need to be finalized and what their finalizers look like) and then, after the object has been finalized, you'll need another GC to re-discover that the object is unreachable before it actually gets collected.
Plus, in the case of a ClassLoader on a Sun JVM, it's likely allocated directly into PermGen. So you'll need not one, but two full PermGen sweeps (one to get the object into the finalization queue, and then a second to actually collect it). Since PermGen sweeps are expensive, I believe the Sun JVM doesn't do them at all with default settings, and even with various GC tunings, it still is pretty reluctant to sweep PermGen (especially if there isn't a lot of other memory pressure).
If instead of forcing (um, I mean, encouraging) a GC using System.gc()
, what if you do something like:
System.gc();
System.runFinalization();
System.gc();
Of course, even this isn't guaranteed to work, but it at least does the minimum necessary work to clean up a large finalization-required object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With