Threadlocal-内存泄露问题

Threadlocal的简单理解和部分源码分析 - Blog|CasonMo

在ThreadLocalMap中,用Entry来保存K-V结构数据的。Entry的构造方法已经限定Entry中key只能是ThreadLocal对象。

1
2
3
4
5
6
7
8
9
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}

Entry继承自WeakReference(弱引用,生命周期只能存活到下次GC前),但只有Key是弱引用类型的,Value并非弱引用。(问题马上就来了)

四种引用类型

  1. 强引用(StrongReference)

类似Object obj = new Object()这类似的引用,强引用在程序代码中普遍存在,只要强引用在,垃圾搜集器永远不会搜集被引用的对象。也就是说,宁愿出现内存溢出(OutOfMemoryError),也不会回收这些对象

  1. 软引用(SoftReference)

在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象

  1. 弱引用(WeakReference)

弱引用也是用来描述非必需对象的,用java.lang.ref.WeakReference类来表示,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。

  1. 虚引用 (PhantomReference)

java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收

虚引用与软引用和弱引用的区别:虚引用必须和引用队列联合使用。

1
2
3
Object object = new Object();
ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);

由于ThreadLocalMap的key是弱引用,而Value是强引用。这就导致了一个问题,ThreadLocal在没有外部对象强引用时,发生GC时弱引用Key会被回收,而Value不会回收。

当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。

如何避免泄漏

为了防止此类情况的出现,我们有两种手段。

  • 使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

    既然Key是弱引用,那么我们要做的事,就是在调用ThreadLocal的get()set()方法时完成后再调用remove方法,将Entry节点和Map的引用关系移除,这样整个Entry对象在GC Roots分析后就变成不可达了,下次GC的时候就可以被回收。

  • JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。


Threadlocal-内存泄露问题
https://cason.work/2024/01/03/Threadlocal-内存泄露问题/
作者
Cason Mo
发布于
2024年1月3日
许可协议