简单明了,说说ThreadLocal内存泄漏

首先,明确下存在泄漏可能的是哪个对象,看一个使用ThreadLocal的例子:

没错,就是我标红的new String("我是大对象"),被set到ThreadLocal中的对象

什么叫内存泄漏呢?说直白点就是这个对象已经没机会再被使用了,但是却一直得不到回收

在本例中,调用完testSetGet()方法后,内存中的引用关系如下图(实线表示强引用,虚线表示弱引用)

其中new ThreadLocal对象被两个引用指向

1.我们自己使用的threadLocal引用,是一个强引用

2.Entry.key引用(Thread对应的ThreadLocalMap的底层数据结构为Entry),Entry的key是一个弱引用类型,从WeakReference继承来的

new String("我是大对象")对象被Entry.value引用,Entry.value是一个强引用;因为ThreadLocalMap是Thread的成员变量,所以其生命周期与Thread一样,只要Thread不销毁,除非这个强引用被置为null,否则就一直不会被回收;

Thread正常运转时,Entry.value只在两种情况下会被置为null,1.显式调用了ThreadLocal.remove(),2.执行ThreadLocalMap的get set方法中,当发现某个Entry的key指向的是null时,就会将Entry.v置为null

1的情况不需要讨论,remove了就不存在泄漏问题了

2的情况,正是ThreadLocalMap将Entry.key设置为WeakReference类型的原因,设计者就是担心使用者没有执行ThreadLocal.remove就离开了,而Thread还在跑着

这时候,前面说的threadLocal这个强引用已经失效,只剩下一个Entry.key指向new ThreadLocal对象,由于Entry.key是弱引用,下次GC时,new ThreadLocal对象就会被带走,Entry.key就变成了null;

但此时new String("我是大对象")还在呢,还好设计者已经在ThreadLocalMap的set get方法中埋下了干掉大对象的雷;

只要后续这个还在运转的Thread调用了任何一个其他ThreadLocal变量的set get方法,new String("我是大对象")就会被干掉,如果没有,那就发生了内存泄漏,泄漏的时间范围与这个Thread的生命周期一致

在实际应用中,基本上我们的线程都在线程池中,所以Thread的生命周期很长。ThreadLocal的设计者,就是担心我们在使用ThreadLocal变量时,没有显式的调用remove方法,所以设计了一个弱引用的Entry.key,就是为了降低内存泄漏的风险,但如果set get方法不被触发,内存泄漏问题就依然存在,所以记得remove;

猜你喜欢

转载自blog.csdn.net/wb_snail/article/details/106115252
今日推荐