Hi everybody,

we are observing memory leaks in our web-application when redeploying in a 
web-context, the problem is that there are references to the web-application
ClassLoader that remain open and prevent class garbage collection of 
singletons, static fields and unloading of Classes.

before you think, *oh no another looser that cannot use a profiler and fix his 
own code* please read the following text, because this is not the case :)

after several days of debugging I found out the cause of this problem, but 
first of all some useful info about our environment.
Our application makes use of OJB 1.0.3 and struts 1.2.7 and is beeing deployed 
on several different web/J2EE servers including tomcat 4.1.31, tomcat 5.5.9,
jboss and WAS.
On all the deplyoment platforms we observed the memory leak.
With the use of profilers and removing single components of the application I 
can exclude that the leak is caused by struts nor by our application, 
furthermore
starting from tomcat 4.1.31 and newer versions of 5.5 the tomcat 
WebappClassLoader is correctly garbage collected when shutting down 
'non-leaking' applications.
This just to show you that the analsys is not completely crap :)

Now to the problem. With the JMP profiler I was able to see that several 
instances of sun.reflect.DelegatingClassLoader were holding references to the 
tomcat
WebappClassLoader and these were the only objects that were still having 
references to the ClassLoader. Since these classes are native (undocumented) 
JVM stuff
I am not able to track where exactly they are allocated.

However, I found out that the use of the static ThreadLocal variable in 
org.apache.ojb.broker.core.PersistenceBrokerThreadMapping is causing the 
problem.
When the PersistenceBrokerFactoryDefaultImpl is beeing used, PersistenceBroker 
instances are wrapped into PersistenceBrokerHandle objects that will register
themselves in the ThreadLocal in PersistenceBrokerThreadMapping.

I could workaround the issue so that the leak will no longer occur by simply 
setting PersistenceBrokerFactory in OJB.properties to a custom implementation 
that
extends the PersistenceBrokerFactoryDefaultImpl, then just overriding the 
method wrapRequestedBrokerInstance(PersistenceBroker(...) as follows:

    protected PersistenceBroker wrapRequestedBrokerInstance(PersistenceBroker 
broker) {
        //return new PersistenceBrokerHandle(broker);
        return broker;
    }

this way the broker will not be wrapped into the PersistenceBrokerHandle and 
not registered in the ThreadLocal variable, however there is a problem:
The brokers in the ThreadLocal variable are used by the classes:
- org.apache.ojb.broker.core.proxy.CollectionProxyDefaultImpl
- org.apache.ojb.broker.core.proxy.IndirectionHandlerDefaultImpl

since we are not using proxies, for us it should not make any difference, but I 
have no idea what it could break if somebody uses Proxies.

Another very ugly workaround is by executing following piece of code when 
shutting down the application:

java.lang.reflect.Field field = 
org.apache.ojb.broker.core.PersistenceBrokerThreadMapping.class.getDeclaredField(
 "currentBrokerMap" );
field.setAccessible( true );                            // Grant access to 
private field
ThreadLocal tl = (ThreadLocal) field.get( null );       // Get value of static 
variable

if ( tl != null ) {                                     
    java.util.HashMap hm = (java.util.HashMap) tl.get();// Get the HashMap of 
brokers opened in CURRENT thread
    if ( hm != null ) {
       hm.clear();                                      // Clear the HashMap
    }
    tl.set( null );                                     // Maybe not necessary, 
reset the map of brokers
}
field.set( null, null );                                // Maybe not necessary, 
set the ThreadLocal to null

This will only work under following conditions:
1) The name of the variable "currentBrokerMap" is the same in other OJB versions
2) The JVM is not using special security policies that prevent the use of 
Field.setAccessible( boolean )
3) THIS WILL ONLY WORK IF THE BROKERS HAVE BEEN OPENED IN THE SAME THREAD THAT 
EXECUTES THE CODE ABOVE

conclusion the code above is not usable, but shows that the problem is 
definitively caused by the use of ThreadLocal.

My guess why this happens is the following:
The Threads are beeing recycled by the web-server, profiling tomcat i can see 
25 Threads that are always ready to answer incoming requests, these threads are
loaded by the top-level ClassLoader, using ThreadLocal variables will bind 
objects created in the web-application ClassLoader context with the top-level
ClassLoader context preventing the web-application beeing correctly garbage 
collected at shutdown. Since the Threads are beeing recycled and never garbage
collected the ThreadLocal variables are not cleaned up and will prevent a 
correct shutdown of the application.

What do you think about it? May this be the cause of the problem?

Now, it is not finished. I found out another (minor) problem. When you 
configure the broker pool to use automagic eviction of unclosed brokers, the 
eviction
thread is never shutdown so that this too will make it impossible to correctly 
shutdown the application.
This is a minor problem since a well programmed application that always 
correctly closes the PersistenceBroker instances will not need the use of the 
eviction
functionality so that this Thread is not started at all.
However it may be useful to introduce a new method in the 
PersistenceBrokerFactory such as shutdown() that in the case of the 
PersistenceBrokerDefaultImpl will
call the releaseAllReferences() method and brokerPool.close() so that any 
running eviction Threads will also be correctly terminated.


Ok, sorry for the long text, but I hope this information will help fix this 
nasty issue and make OJB even better as what it already is.
for questions or if you need more information, just ask me.

cheers *pants heavly*
Danilo




---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to