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]