I've been battling some memory leaks, and I'm currently baffled by this issue. There's a web application classloader that was supposed to be garbage collected, but it isn't (even after I fixed several leaks). I dumped the heap with jmap and browsed it with jhat, found the classloader and checked the rootset references.
If I exclude weak refs, the list is empty! How is that possible, since an object held only by weak references should get garbage collected? (I performed GC many times in jconsole)
If I include weak refs, I get a list of references, all of which come from one of the following fields:
- java.lang.reflect.Proxy.loaderToCache
- java.lang.reflect.Proxy.proxyClasses
- java.io.ObjectStreamClass$Caches.localDescs
- java.io.ObjectStreamClass$Caches.reflectors
- java.lang.ref.Finalizer.unfinalized
I couldn't find any reason why any of those references should prevent garbage collecting the classloader. Is it a gc bug? Special undocumented case? jmap/jhat bug? Or what?
And the weirdest thing... after sitting idle and gc-ing from time to time for about 40 min, without changing anything, it finally decided to unload classes and collect the classloader.
Note:
If you make a claim about delayed collection of classloaders or weak references, then please specify the circumstances in which it happens, and ideally:
- provide a link to an authoritative article that supports your claim
- provide a sample program that demonstrates the behavior
If you think the behavior is implementation-dependent, then please focus on what happens in the oracle or icedtea jvm, version 6 or 7 (pick any one of them and be specific).
I'd really like to get to the bottom of this. I actually put some effort into reproducing the issue in a test program, and I failed - the classloader was instantly collected on System.gc() every time unless there was a strong reference to it.