Situation:
- Need a cache of an expensive-to-create and non-thread-safe external resource
- The resource requires explicit clean up
- The termination of each thread cannot be hooked, but that of the application can
- The code also runs in a Servlet container, so caches that cause a strong reference from the system class loader (e.g.
ThreadLocal
) cannot be directly used (see edit below).
Thus to use a ThreadLocal
, it can only hold WeakReference
s to the resource and a separated collection of strong references has to be kept. The code quickly gets very complicated and creates a memory leak (as the strong reference is never removed after thread death).
ConcurrentHashMap
seems to be a good suit, but it also suffers from the memory leak.
What other alternatives are there? A synchronised WeakHashMap??
(Hopefully the solution can also be automatically initialised using a given Supplier
just like ThreadLocal.withInitial()
)
Edit:
Just to prove the class loader leak is a thing. Create a minimal WAR project with:
public class Test {
public static ThreadLocal<Test> test = ThreadLocal.withInitial(Test::new);
}
index.jsp:
<%= Test.test.get() %>
Visit the page and shutdown the Tomcat and you get:
Aug 21, 2015 5:56:11 PM org.apache.catalina.loader.WebappClassLoaderBase checkThreadLocalMapForLeaks
SEVERE: The web application [test] created a ThreadLocal with key of type [java.lang.ThreadLocal.SuppliedThreadLocal] (value [java.lang.ThreadLocal$SuppliedThreadLocal@54e69987]) and a value of type [test.Test] (value [test.Test@2a98020a]) 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.