Class loader leaks
This page describes some of the findings while trying to figure out class loader leaks in deegree 2 and 3.
Contents
1. What's a class loader leak?
When you deploy a context in a web application container, it (at least Tomcat) usually creates a new class loader to load your webapp. If you redeploy it, the class loader is "released" and should be garbage collected. Class loaders hold references to their loaded classes, and the classes hold references to their class loader. Now if for some reason a class loaded by the system class loader holds a reference to such a class or class loader, ALL the classes cannot be garbage collected. Multiple redeployments result in classes that are loaded multiple times. Every such "instance" of Class still holds the references to its static fields!
So what you have to do is to remove these references when your app is unloaded. This can be done in the destroy() method of the servlet class.
2. Tomcat 5.5/6
These findings are based upon findings using Tomcat 6, but most should also be correct for Tomcat 5.5.
Tomcat 5.5 sometimes only collects every second context, and leaves one context every reloading twice. Tomcat 6 leaves only one extra context in at a time. It is unknown how this can occur.
3. Known issues
3.1. PostgreSQL libraries
Using postgresql-8.3-603.jdbc3.jar, you'll find that it includes a META-INF entry that loads the org.postgresql.Driver (see below why this is bad).
3.2. org.postgresql.Driver
When loading this class (eg. with Class.forName or by deploying the 8.3 .jar), you'll have to deregister it in java.sql.DriverManager:
3.3. Logging
When using logging frameworks, you'll have to manually unregister logging classes. Example:
That last Introspector call is said to have something to do with the pattern used to format the log entries, keeping it in does not have any unwanted effects.
3.4. ThreadLocals
Before using ThreadLocal think about a way around using it. If you still want to use it, you'll have to make sure that its value is cleaned once you don't need it any more, using #remove(). In particular, make sure that you clean them up after Servlet#init() is complete, and after #doGet() and #doPost()are finished (use finally blocks!). Using InheritableThreadLocal is a particularly bad idea, especially if control is subsequently let gone of into third party code which spawns its own threads. These threads will usually fail to clean up the inherited ThreadLocal.
3.5. Thread pools
If you're using the thread pools from java.util.concurrent, you'll have to make sure these threads are stopped when the servlet is destroyed. This can be accomplished by calling ExecutorService#shutdown(). Make sure all other Threads are also stopped once you're done.
3.6. Other libraries
Some libraries (JAI, xerces/Java internal DOM) will have the effect that one more instance will remain in memory upon reloading. Example: you use JAI, and redeploy 10 times. You'll have two copies of the WebappClassLoader in memory. You use JAI and DOM, and you'll have three (when reloading at least twice).
3.7. JAXB
2.1.3 seems to have a leak (some class in com.sun.xml.internal.bind.v2.model.impl.* seems to hold references to everything it loaded). Recent 2.1.x nightly builds fix the problem (but are not included in 1.6.0_10).
3.7.1. JAXB ThreadLocals
JAXB unfortunately creates its own ThreadLocal instances. deegree 3 currently loads its configuration upon servlet initialization time, so it must make sure these ThreadLocals are destroyed once initialization finishes. We did not observe any negative effect until now, but have a look yourself how ugly the workaround is (I've always wanted to use #setAccessible(true)...).
try {
Field f = ClassFactory.class.getDeclaredField( "tls" );
f.setAccessible( true );
( (ThreadLocal<?>) f.get( null ) ).set( null );
f = Coordinator.class.getDeclaredField( "activeTable" );
f.setAccessible( true );
( (ThreadLocal<?>) f.get( null ) ).set( null );
} catch ( java.lang.SecurityException e ) {
LOG.error( "Failed to plug thread local leaks of jaxb." );
LOG.trace( "Stack trace:", e );
} catch ( NoSuchFieldException e ) {
LOG.error( "Failed to plug thread local leaks of jaxb." );
LOG.trace( "Stack trace:", e );
} catch ( IllegalArgumentException e ) {
LOG.error( "Failed to plug thread local leaks of jaxb." );
LOG.trace( "Stack trace:", e );
} catch ( IllegalAccessException e ) {
LOG.error( "Failed to plug thread local leaks of jaxb." );
LOG.trace( "Stack trace:", e );
}
