Is this very likely to create a memory leak in Tomcat?
The message is actually pretty clear: something creates a ThreadLocal
with value of type org.apache.axis.MessageContext
- this is a great hint. It most likely means that Apache Axis framework forgot/failed to cleanup after itself. The same problem occurred for instance in Logback. You shouldn't bother much, but reporting a bug to Axis team might be a good idea.
Tomcat reports this error because the ThreadLocal
s are created per HTTP worker threads. Your application is undeployed but HTTP threads remain - and these ThreadLocal
s as well. This may lead to memory leaks (org.apache.axis.MessageContext
can't be unloaded) and some issues when these threads are reused in the future.
For details see: http://wiki.apache.org/tomcat/MemoryLeakProtection
I added the following to @PreDestroy method in my CDI @ApplicationScoped bean, and when I shutdown TomEE 1.6.0 (tomcat7.0.39, as of today), it clears the thread locals.
/* * To change this template, choose Tools | Templates * and open the template in the editor. */package pf;import java.lang.ref.WeakReference;import java.lang.reflect.Array;import java.lang.reflect.Field;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * * @author Administrator * * google-gson issue # 402: Memory Leak in web application; comment # 25 * https://code.google.com/p/google-gson/issues/detail?id=402 */public class ThreadLocalImmolater { final Logger logger = LoggerFactory.getLogger(ThreadLocalImmolater.class); Boolean debug; public ThreadLocalImmolater() { debug = true; } public Integer immolate() { int count = 0; try { final Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); threadLocalsField.setAccessible(true); final Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals"); inheritableThreadLocalsField.setAccessible(true); for (final Thread thread : Thread.getAllStackTraces().keySet()) { count += clear(threadLocalsField.get(thread)); count += clear(inheritableThreadLocalsField.get(thread)); } logger.info("immolated " + count + " values in ThreadLocals"); } catch (Exception e) { throw new Error("ThreadLocalImmolater.immolate()", e); } return count; } private int clear(final Object threadLocalMap) throws Exception { if (threadLocalMap == null) return 0; int count = 0; final Field tableField = threadLocalMap.getClass().getDeclaredField("table"); tableField.setAccessible(true); final Object table = tableField.get(threadLocalMap); for (int i = 0, length = Array.getLength(table); i < length; ++i) { final Object entry = Array.get(table, i); if (entry != null) { final Object threadLocal = ((WeakReference)entry).get(); if (threadLocal != null) { log(i, threadLocal); Array.set(table, i, null); ++count; } } } return count; } private void log(int i, final Object threadLocal) { if (!debug) { return; } if (threadLocal.getClass() != null && threadLocal.getClass().getEnclosingClass() != null && threadLocal.getClass().getEnclosingClass().getName() != null) { logger.info("threadLocalMap(" + i + "): " + threadLocal.getClass().getEnclosingClass().getName()); } else if (threadLocal.getClass() != null && threadLocal.getClass().getName() != null) { logger.info("threadLocalMap(" + i + "): " + threadLocal.getClass().getName()); } else { logger.info("threadLocalMap(" + i + "): cannot identify threadlocal class name"); } }}
The key "Transactional Resources" looks like you are talking to the database without a proper transaction. Make sure transaction management is configured properly and no invocation path to the DAO exists that doesn't run under a @Transactional annotation. This can easily happen when you configured transaction management on the Controller level but are invoking DAOs in a timer or are using @PostConstruct annotations. I wrote it up here http://georgovassilis.blogspot.nl/2014/01/tomcat-spring-and-memory-leaks-when.html
Edit: It looks like this is (also?) a bug with spring-data-jpa which has been fixed with v1.4.3. I looked it up in the spring-data-jpa sources of LockModeRepositoryPostProcessor which sets the "Transactional Resources" key. In 1.4.3 it also clears the key again.