0

I have the following classes:

public class AppContext {
        
    private static final ThreadLocal<AppContextData> contextHolder = new ThreadLocal<AppContextData>() {
        
        @Override
        protected AppContextData initialValue() {
            return new AppContextData();
        }
    };
    
    public static AppContextData get() {
        return contextHolder.get();
    }
    
    public static void unset() {
        contextHolder.remove();
    }
}

public class AppContextData {
   //many getter and setter methods
}

The website starts using the ThreadLocal object in an interceptor and the object is used through out a web request.

public class MyIntercepter extends HandlerInterceptorAdapter {
            
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  {
    
        AppContext.get();
        //..... 
    }
}

I run this website on Tomcat 8.x and I find out that the log has the following error message.

20-May-2016 22:43:53.657 SEVERE [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [ROOT] created a ThreadLocal with key of type [my.organization.data.AppContext$1] (value [my.organization.data.AppContext$1@31d4463a]) and a value of type [my.organization.data.AppContextData] (value [my.organization.data.AppContextData@e89d5f4]) 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.

How can I fix this issue?

halfer
  • 19,824
  • 17
  • 99
  • 186
curious1
  • 14,155
  • 37
  • 130
  • 231
  • 1
    Make sure AppContext.unset() is called. Tomcat wants you to explicitely remove the ThreadLocal. – JanPl May 21 '16 at 06:30

1 Answers1

2

JanPi already said in the comments what we have to do. In case someone wonders about the how, here it is:

public class MyIntercepter extends HandlerInterceptorAdapter {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  {
        AppContext.get();
        //..... 
    }

    public void afterCompletion(
        HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AppContext.unset();
    }

    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
        // only relevant when a handler starts an asynchronous request.
        AppContext.unset();
    }
}

The postHandle method looks like a good place, but afterCompletion is better because it's called even when the handler fails to process the request correctly (aka an exception occurred).

dube
  • 4,898
  • 2
  • 23
  • 41