Hi,
On 29 January 2014 09:16, Felix Meschberger <[email protected]> wrote:
> Hi
>
> Am 29.01.2014 um 09:53 schrieb Ian Boston <[email protected]>:
>
>> Hi,
>> Is it possible, in code running in a bundle, to detect if the OSGi
>> container has a classloader that is being pinned and should have been
>> garbage collected.
>
> Well, that bundle would have to analyze class loaders and memory dumps. You
> certainly can do that if you wish, but at what price ?
>
>>
>> I have recently found code (in bundle A) that registered a
>> implementation class with a service outside its own bundle (bundle B)
>> but forgot to deregister that class when bundle A restarts. (during
>> startup or reconfiguration)
>
> This, I don't understand: A registers a service implemented by a class
> provided by B but does not unregister that service when A stops ?
>
> Well, that is not really a problem because when a bundle is stopped, all
> services registered by that stopping bundle are unregistered by the framework
> (unless unregistered by the bundle already) -- yes, this is a safety net.
>
> The problem really comes in if A registers a service implemented by one of
> its own classes, B uses it and does not let go of the service object when the
> service is unregistered.
thats what I meant.
eg JCR observation.
>
> Or worse: If B is calling in to the service S and gets an object O back whose
> class is provided by A and B does not let go off O if S when S is
> unregistered.
>
>>
>> Bundle B has a copy of the class and a reference to the classloader.
>> The original bundle A has gone, but the classloader remains. I have a
>> feeling that the classloader might be weakly referenced by the
>> container through wiring, but could be wrong.
>
> Actually, in my last example (object O from S or even holding on to service
> object S), B holds the object which has a strong reference to the class which
> in turn has a strong reference to the class loader which in turn has strong
> references to all classes owned by that class loader which in turn -- oh my
> ....
>
I am seeing something like that when I look at the heap dump.
>>
>> This appears to causes 4 problems:
>>
>> A memory leak.
>>
>> B I think, but I am not certain, that the classloader is referenced
>> (weak(ly)) by the JSP compiler causing certain calls to
>> classloader.getResourceAsStream(className.replace('.','/')+".class");
>> to return null which makes the JSP compiler think the class is a
>> package. Reported in the logs, fixed by a JSP bundle restart or
>> container restart.
>>
>> C Instability, although I am even less certain about this and don't
>> have evidence.
>>
>> D Very occasionally a broken container on startup.
>
> A broken container on startup ? Unless this situation happens due to bundles
> A and/or B starting/stopping during startup.
That happens, multiple times.
>
>>
>> I can isolate the problem by taking a heap dump and analysing the heap
>> for live classes from more than one classloader where the class is not
>> embeded.
>>
>> Is there a way of doing this in the container when it is running,
>> ideally exposed through the sling console ?
>>
>> If not:
>> I have been thinking about listening to bundles start and stop and
>> holding weak references to their bundles and classloaders to identify
>> classloaders that exist with no bundle.
>>
>> Although it would be better if I could load a bundle that locates all
>> live classloaders and builds a list of classes loaded from more than
>> one classloader.
>>
>> Any ideas welcome ?
>
> There is no such thing as a class loader registry. And you cannot walk down
> the class loader hierarchy since parent class loaders don't know about their
> "children". Even worse: In OSGi class loaders are connected to each other
> through OSGi wiring. So you cannot find all class loader instances just like
> that.
<aside>
Does the wiring have strong references between classloaders or is it
via bundles ?
Can you walk the wiring with reflection ?
</aside>
>
> What you might do is get the class loader of a stopping bundle and see
> whehter it is still referenced. But this would probably require a GC run (or
> two or more) and access to JVM internal memory management.
I see the the Wiring interfaces have some indication of the state of
the owning bundle, including if its alive or not, so this might be an
option, although it would need to be running all the time.
>
> Maybe we could implement a bundle which can take memory snapshots and allow
> -- in the web console or so -- analyzing that dump with the Eclipse dump
> analyzer code ?
That goes part of the way, however there are a lot of false positives
which require inspection to remove from the list. In my first dump I
got 3600 classes so I started with com.adobe.* as a filter, then
ignored any class from an embedded jar, then ignored anything with 2
classloaders, as I had caused more than 2 reloads.
However, the dump analyzer takes several minutes to build its indexes
from a dump and then takes several minutes to run the report. Not
something that you want to do on a live system.
Best Regards
Ian
>
> Regards
> Felix