仅在堆栈溢出方面就有几个(多个)问题是关于ThreadLocals如何导致内存泄漏的。我不想再加进去了。但是,在阅读了它们和一连串的答案之后,在保留的ThreadLocal地图条目值上发生了巨大的内存泄漏,而我只是通过将ThreadLocal<T>改为ThreadLocal<WeakReference<T>>来解决这个问题,现在我想知道为什么不总是这样做,或者更确切地说,为什么(在世界上)原始ThreadLocal的设计者使用WeakReference作为密钥(我认为是线程),而不是为了这个值呢?为什么在ThreadLocal地图上没有至少有一个清除的管家会删除丢失密钥的条目呢?
我知道“为什么”问题有被标记和关闭的危险。但我们从问为什么学到了?也许有人知道我在忽略什么。
下面是我建议永远使用的东西,而不是裸露的ThreadLocal:
import java.lang.ref.WeakReference;
public class PragmaticThreadLocal<T> extends ThreadLocal<T> {
public T get() {
final WeakReference<T> ref = (WeakReference<T>)super.get();
return ref == null ? null : ref.get();
}
public void set(T value) {
super.set((T)(new WeakReference<T>(value)));
}
}现在,这似乎解决了我的问题,我想,如果我们走了这么远,为什么这不被认为是JDK中的一个bug,并在将来修复了所有ThreadLocals都应该有一个WeakReference值呢?
然后,第一个评论和回答告诉我,我认为WeakReference会有帮助是错误的。它确实可以防止内存泄漏,但它使变量毫无用处,因为它连一个GC都无法生存!而且,我错误地认为,如果线程消失了,ThreadLocal就会被持有,不,当线程消失时,ThreadLocal也会这样做。问题是,如果线程仍然存在,那么ThreadLocal仍然存在。
很多时候,我们使用静态ThreadLocal,这不是什么问题(除了您启动、停止和“重新部署”的web应用程序中的perm泄漏之外)。我的问题是,我使用ThreadLocal作为实例变量,当对象实例消失时,它本身就没有清理。
使用终结器方法清除(删除)这些ThreadLocal变量是否是正确的方法?我记得使用终结器来清理文件或其他稀缺的资源是个问题,不应该这样做,所以对于文件,我们通常可以在试图捕获-最终块的最后一部分关闭它们。但是对于线程局部变量,我们不能做到这一点,因为如果我们可以将它们放在一个语句块中(可以只使用局部变量),我们就不需要它们成为ThreadLocal。因此,清除终结器中的ThreadLocals是否正确?在20年的Java编码中,我很少使用终结器方法。我想我现在该开始了?
但是,意识到ThreadLocal的使用与内存泄漏有关,我感到困惑的是,为什么没有标准的食谱解决方案来解决一般的问题?难道没有一些最佳实践编码模式可以解决堆和perm内存泄漏的所有问题吗?
在我自己的例子中,我没有使用任何ThreadLocal就解决了我的问题,相反,我只是将线程扩展到了我自己的类,这个类可以保存我所需要的东西,然后在线程离开时离开,或者在下次使用同一个线程来服务其他任务时,至少要进行绑定和清理。但是我仍然在其他地方使用ThreadLocal,我想就它们的使用提出一个标准的实践。同样,就像关闭文件一样。
再说一次,终结器是正确的选择吗?
奇怪的是,为什么这是必要的。当一个对象持有一个ThreadLocal,并且该对象消失时,现在ThreadLocal本身应该有资格进行垃圾收集。但是它已经在ThreadLocal映射中创建了一个条目,并且当收集ThreadLocal时,它不会自动删除它的ThreadLocal映射条目,这是真的吗?如果是的话,那不是个虫子吗?还是又是我的误会?
因为如果终结器方法对我有用,那么为什么ThreadLocals没有一个终结器本身来删除它们的地图条目?
class MyThing {
private ThreadLocal _threadLocal;
public void myMethod() {
_threadLocal = new ThreadLocal();
_threadLocal.set(value);
}
protected void finalize() {
if(_threadLocal != null)
_threadLocal.remove();
}
}因此,如果我能够做到这一点,那么为什么ThreadLocal没有终结器方法,或者,同样,我可以使用这样的PragmaticThreadLocal:
public class PragmaticThreadLocal<T> {
protected void finalize() {
this.remove();
}
}我该这么做吗?我是不是又搞错了,这个清理工作已经被ThreadLocal处理好了?
在这种情况下,为什么我会有一个内存泄漏?
发布于 2022-06-10 07:20:22
如果这个值总是一个WeakReference,那么在很多典型的用例中,它都是对这个值的唯一引用,那么它就会立即符合GC的条件--这又有什么用呢?
参见JavaDocs of ThreadLocal中的示例
import java.util.concurrent.atomic.AtomicInteger; public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new AtomicInteger(0); // Thread local variable containing each thread's ID private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextId.getAndIncrement(); } }; // Returns the current thread's unique ID, assigning it if necessary public static int get() { return threadId.get(); } }
这将不再适用于你的方法。它可能会在每次返回一个新的值,以不确定的方式,取决于GC。
https://stackoverflow.com/questions/72570024
复制相似问题