Versions Compared

Key

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

...

But it does not give any clue about what caused the leak, we would need to make a heapdump and analyse it with some tool like Eclipse MAT.

ThreadLocal pseudo-leak

Suppose we have the same MyCounter class as above (in the webapp) and the following servlet :

No Format

public class LeakingServlet extends HttpServlet {
	private ThreadLocal<MyCounter> myThreadLocal = new ThreadLocal<MyCounter>();

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		MyCounter counter = myThreadLocal.get();
		if (counter == null) {
			counter = new MyCounter();
			myThreadLocal.set(counter);
		}

		response.getWriter().println(
				"The current thread served this servlet " + counter.getCount()
						+ " times");
		counter.increment();
	}

	@Override
	public void destroy() {
		super.destroy();
		// normally not needed, just to make my point
		myThreadLocal = null;
	}
}

Notice that the ThreadLocal instance is referenced through an instance variable, not a static one.

Sun's implementation of ThreadLocal (and WeakHashMap) too is such that ThreadLocalMap entries whose key is GCed are not immediately removed. (The key is a weak reference to the ThreadLocal instance, see java.lang.ThreadLocal.ThreadLocalMap.Entry<T> in JDK 5/6. And there's no daemon thread waiting on a ReferenceQueue). Instead, it's only during subsequent uses of ThreadLocal features that each Thread removes the abandoned ThreadLocalMap.Entry entries (see ThreadLocalMap.expungeStaleEntries().

If many threads were used to serve our leaking webapp, but after we stop it only a couple of threads are enough to serve other webapps, one could have some threads that are no longer used, waiting for some work. Since those threads are blocked, they have no interaction with their ThreadLocalMap (i.e. there's no ThreadLocal value bound to them or removed), so that there's no opportunity to expungeStaleEntries().

Tomcat 6.0.24 "speeds up" the removal of stale entries (and thus fixes the pseudo-leak), by calling expungeStaleEntries() for each thread that has some stale entries.

Threads ContextClassLoader

...