About memory leaks when undeploying a web application
A new type of memory leak
SEVERE: The web application [] created a ThreadLocal with key of type [org.springframework.core.NamedThreadLocal] (value [Transactional resources] ... but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
Apparently a Spring-managed thread local variable is not being cleaned up. After a binary search over all possible components it turned out to be this particular instance:
@Component public class SomeComponent{ @Autowired SomeSpringDataJpaDao dao; @PostConstruct public void setup(){ dao.doSomething(); } }
The solution is (almost) fortunately quite trivial: add an @Transactional annotation to either the component or the setup() method.
Note that Spring-data-jpa 1.4.3 seems to have fixed a related bug in LockModeRepositoryPostProcessor.invoke() which used to set the “Transactionl resources” thread local variable but never cleared it.
Update 2018.05.27
I’m using this Spring application event listener to clean up lingering C3P0 and MySql threads. This is no original research; I stole the bits and pieces from around the web a while ago but, unfortunately, don’t recall where from. I’m sorry 😦
public class DeregisterJdbcDrivers implements ApplicationListener { Logger logger = Logger.getLogger(DeregisterJdbcDrivers.class); void stopMySql() { try { logger.info("Shutting down MySQL cleanup thread"); AbandonedConnectionCleanupThread.shutdown(); } catch (Throwable t) { } } void unregisterJdbcDrivers() { Enumeration drivers = java.sql.DriverManager.getDrivers(); while (drivers.hasMoreElements()) { java.sql.Driver driver = drivers.nextElement(); logger.info("Unregistering JDBC driver " + driver); try { java.sql.DriverManager.deregisterDriver(driver); } catch (Throwable t) { } } } public void stopC3P0() { C3P0Registry.getNumPooledDataSources(); for (PooledDataSource dataSource : (Collection) C3P0Registry.getPooledDataSources()) { try { dataSource.close(); } catch (Exception e) { logger.error(e); } } } @Override public void onApplicationEvent(ContextClosedEvent event) { stopC3P0(); stopMySql(); unregisterJdbcDrivers(); try { Thread.sleep(2000L); } catch (Exception e) { } } }
Resources
[1] Dealing with “java.lang.OutOfMemoryError: PermGen space” error
http://stackoverflow.com/questions/88235/dealing-with-java-lang-outofmemoryerror-permgen-space-error
[2] Memory Leak Protection in Tomcat 7
http://java.dzone.com/articles/memory-leak-protection-tomcat