threadlocal内存泄漏的原因

2023-03-21 11:01:09 浏览数 (2)

ThreadLocal的原理:

ThreadLocal的set实际实在当前线程对象里创建了一个内部变量ThreadLocalMap<ThreadLocal,object> ,ThreadLocalMap的key是ThreadLocal的引用。

造成泄漏的原因:

由于ThreadLocal对象是弱引用,如果外部没有强引用指向它,它就会被GC回收,导致Entry的Key为null

如果当前的情况下在栈中将threadlocal1的引用设置为null,强引用1将会失效,那堆中的threadlocal1对象因为ThreadLocalMap的key对它的引用是弱引用,将会在下一次gc被回收,那就会出现key变成null,如果这时value外部也没有强引用指向它,那么value就永远也访问不到了,按理也应该被GC回收,但是由于ThreadLocalMap.Entry对象还在强引用value,导致value无法被回收,这时「内存泄漏」就发生了,value成了一个永远也无法被访问,但是又无法被回收的对象。

解决办法:

1:将ThreadLocal设置为空之前,执行remove()方法,会将key为空的键值对清空

2:尽量将ThreadLocal设置成static

3: 非必要尽量不要在ThreadLocal中放大对象

ThreadLocal做出的努力

ThreadLocal不是洪水猛兽,不要听到「内存泄漏」就不敢使用它,只要你规范化使用是不会有问题的。再者,就算你不规范使用,ThreadLocal也做出了很多努力来最大程度的帮你避免发生「内存泄漏」。

前面已经说过,由于Key是弱引用,因此ThreadLocal可以通过key.get()==null来判断Key是否已经被回收,如果Key被回收,就说明当前Entry是一个废弃的过期节点,ThreadLocal会自发的将其清理掉。

hreadLocal会在以下过程中清理过期节点:

调用set()方法时,采样清理、全量清理,扩容时还会继续检查。 调用get()方法,没有直接命中,向后环形查找时。 调用remove()时,除了清理当前Entry,还会向后继续清理。

为什么这里要用弱引用:

网上有的文章将ThreadLocal内存泄漏的原因怪罪于Entry的Key的弱引用,这个说法是极其错误的!

不用弱引用就能避免「内存泄漏」了吗?当然不是!!! 恰恰相反,使用弱引用是JDK在尽量避免程序出现「内存泄漏」,如下代码:

代码语言:javascript复制
public class Test {
    public static void main(String[] args) {
        ThreadLocal threadLocal = new ThreadLocal();
        threadLocal.set(new Object());
        threadLocal = null;
    }
}

创建一个ThreadLocal对象,并设置一个Object对象,然后将其置空。如果Key不是弱引用的话,threadLocal无法被回收,也无法被访问,object无法被回收,也无法被访问,Key和Value同时出现了「内存泄漏」

0 人点赞