Search code examples
javasessiontomcatmemory-leaksthread-local

Is there a way to close all the ThreadLocals (saving Session object) in contextDestroyed() method using ContextListener at the time of shutdown?


I am facing the memory leak(TOMCAT v9) in my application regarding the Threadlocals object as below:

25-Sep-2019 17:40:34.752 SEVERE [Thread-10] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@7c606bab]) and a value of type [org.hibernate.internal.SessionImpl] (value [SessionImpl(56310373)]) 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.

I have tried to close the session object, everytime we opened the session object , but due to lack of standards used in the coding i am facing various Hibernate level exception as for some objects are lazily fetched, and closing the session results in unexpected exceptions.

Now, i am trying to achieve the same thing using a contextListener if possible, without worrying about closing each session object.

I have tried using System.gc() but it doesn't seem to free those ThreadLocal objects.

Could you please suggest some way to get rid of these objects at the time of shutdown using Listener or any other way possible?


Solution

  • You should close the Hibernate session and remove it from the ThreadLocal when the web request is done.

    Since you seem to have issues with the lazy loading while generating the response (JSP?), you need to do that after the response has been completely generated.

    You can do that in a Servlet Filter. Make sure this is before any other filters you have, so the session is closed as the last step.

    public final class SessionCloseFilter implements Filter {
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        public void destroy() {
        }
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            try {
                chain.doFilter(request, response);
            } finally {
                try {
                    // Close and remove session here
                } catch (Exception e) {
                    // Log but don't throw the exception
                }
            }
        }
    
    }