【发布时间】:2015-11-15 11:18:38
【问题描述】:
情况:
- 需要一个创建成本高且非线程安全的外部资源的缓存
- 资源需要显式清理
- 不能hook每个线程的终止,但是可以hook应用程序的终止
- 代码也在 Servlet 容器中运行,因此无法直接使用导致来自系统类加载器(例如
ThreadLocal)的强引用的缓存(请参阅下面的编辑)。
因此要使用ThreadLocal,它只能将WeakReferences 保存到资源中,并且必须保留一个单独的强引用集合。代码很快变得非常复杂并造成内存泄漏(因为强引用在线程死亡后永远不会被删除)。
ConcurrentHashMap 看起来不错,但它也存在内存泄漏问题。
还有哪些其他选择?一个同步的 WeakHashMap??
(希望解决方案也可以使用给定的Supplier 自动初始化,就像ThreadLocal.withInitial() 一样)
编辑:
只是为了证明类加载器泄漏是一回事。创建一个最小的 WAR 项目:
public class Test {
public static ThreadLocal<Test> test = ThreadLocal.withInitial(Test::new);
}
index.jsp:
<%= Test.test.get() %>
访问该页面并关闭Tomcat,您会得到:
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.
【问题讨论】:
-
资源是绑定到线程还是可以从不同的线程使用,一次只能使用一个?
-
它们不必绑定到特定线程:)
-
谁持有对
ThreadLocal的强引用? -
ThreadLocal只是在我的 WAR 类的一个字段中。单独的List用于保存(强引用)WeakReferences(在ThreadLocal中)引用的所有资源实例。这是为了防止每个Thread引用的ThreadLocalMap对WAR 的类加载器具有强引用链(从而导致泄漏)。 -
线程不持有对
ThreadLocal实例的强引用。内部ThreadLocalMap使用弱引用。你实际上是导致你试图解决的问题。
标签: java multithreading caching