db-ojb-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Danilo Tommasina (No Signature)" <dtommas...@risksys.com>
Subject ThreadLocal causing memory leak
Date Thu, 09 Jun 2005 13:02:29 GMT
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: ojb-user-unsubscribe@db.apache.org
For additional commands, e-mail: ojb-user-help@db.apache.org


Mime
View raw message