Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Threads spawned by classes loaded by the common classloader

TODO : example with the Evictor thread of dbcp

Threads spawned by JRE classes

Child classloaders

...

Suppose, we have the Commons Pool library in the classpath of the server (e.g. the jar is in tomcat/lib), and the following servlet :

No Format

public class LeakingServlet extends HttpServlet {
	private GenericObjectPool objectPool;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		response.getWriter().println("Number of idle objects in the pool :"+objectPool.getNumIdle());
	}

	@Override
	public void init() throws ServletException {
		objectPool = new GenericObjectPool();
		objectPool.setFactory(new BasePoolableObjectFactory() {
			private AtomicInteger counter = new AtomicInteger(0);

			@Override
			public Object makeObject() throws Exception {
				String str = "Object #" + counter.incrementAndGet();
				System.out.println("Creating "+str);
				return str;
			}

			@Override
			public void destroyObject(Object obj) throws Exception {
				System.out.println("Destroying "+obj);
			}
		});
		objectPool.setMinIdle(3);
		objectPool.setTimeBetweenEvictionRunsMillis(1000);
	}

	@Override
	public void destroy() {
		try {
			objectPool.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

The call to GenericObjectPool.setTimeBetweenEvictionRunsMillis) actually starts or reuses a java.lang.Timer shared between all GenericObjectPool instances. As long as a pool is running and at least one pool is using the timer eviction feature, the Timer lives.

If there's no other webapp using commons-pool, there's no leak : when we stop the webapp, the servlet is stopped and the pool is closed. If it was the only pool in use, the Timer thread is also stopped and there's no leak.

Now, imagine that there are 2 webapps using commons-pool with the timer eviction feature (imagine the above servlet is deployed in 2 webapps A and B). Suppose webapp A is deployed, then B. Since the commons-pool jar is shared between both webapps, only one Timer thread is spawned, with 2 TimerTask, one for each webapp, to handle the eviction of each pool instance.

Then, if we stop webapp A, boom it's leaking! the Timer thread has its context classloader set to the WebAppClassLoader of webapp A. This is somehow a bug of commons-pool, but tomcat 6.0.24 tries to help :

No Format

INFO: HTMLManager: stop: Stopping web application at '/testWeb'
Destroying Object #3
Destroying Object #2
Destroying Object #1
Mar 21, 2010 9:26:36 PM org.apache.catalina.loader.WebappClassLoader clearReferencesStopTimerThread
SEVERE: A web application appears to have started a TimerThread named [Timer-0] via the java.util.Timer API but has failed to stop it. To prevent a memory leak, the timer (and hence the associated thread) has been forcibly cancelled. 

So the leak is fixed, but unfortunately there's a side effect : it broke webapp B eviction timer.

(TODO: file a bug report to make the clearReferencesStopTimerThread optional and disabled by default)

Note: as of 6.0.24, by default tomcat stops threads of class java.util.TimerThread whose context classloader is the WebAppClassLoader of the app being stopped. It does not stop other threads, it only warns about them. It can try to stop them if the clearReferencesStopThreads option of the standard context is set to true.

Threads spawned by JRE classes

Just like third-party libraries may spawn threads that provoke leaks, some JRE classes also spawn threads that inherit from the current class loader and thus provoke leaks.

Instead of trying to stop such threads, tomcat prefers to force the creation of such threads when the container is started, before webapps are started. The JreMemoryLeakPreventionListener does it for a few known offenders in the JRE.

Child classloaders

When an app is stopped, Tomcat 6.0.24 detects leaks caused by ThreadLocal}}s and Threads context classloader only by checking for the current {{WebAppClassLoader. If a child classloader is involved, the leak is not detected. That should be improved in a future release.

static class variables

When an app is stopped, Tomcat (even before 6.0.24) nullifies the value of all static class variables of classes loaded by the WebAppClassLoader. In some cases, it may fix a classloader leak (for example because of a custom ThreadLocal class, see above), but even if we still have a leak, it may decrease the amount of memory lost:

Imagine a class with the following variable :

No Format

private final static byte[] BUFFER = new byte[1024*1024]; //1MB buffer

Normally, the 1MB buffer should be freed when the app is stopped, but only if the classloader itself can be garbage-collected. Since there are still possibilities to have a leak of the classloader, clearing the BUFFER variable allows to recover 1MB of memory.

LogFactory

JavaBean Introspector cache

...