
当把threadlocal变量置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收。这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,而这块value永远不会被访问到了,所以存在着内存泄露。
只有当前thread结束以后,current thread就不会存在栈中,强引用断开,Current Thread、Map value将全部被GC回收。最好的做法是不在需要使用ThreadLocal变量后,都调用它的remove()方法,清除数据。
为什么使用弱引用而不是强引用?
key 使用强引用
对ThreadLocal对象实例的引用被置为null了,但是ThreadLocalMap还持有这个ThreadLocal对象实例的强引用,如果没有手动删除,ThreadLocal的对象实例不会被回收,导致Entry内存泄漏。
key 使用弱引用
对ThreadLocal对象实例的引用被被置为null了(局部变量声明的**ThreadLocal对象),由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal的对象实例也会被回收。value在下一次ThreadLocalMap调用set,get,remove都有机会被回收(ThreadLocal自我保护机制)。
比较两种情况,我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障,弱引用ThreadLocal实例不会内存泄漏,对应的value在下一次ThreadLocalMap调用set,get,remove的时候会被清除。
因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key**就会导致内存泄漏,而不是因为弱引用。
为什么使用static final修饰?
- static:节省空间
- 避免被修改引用,造成读不到之前设置的值
内存泄漏真正原因:正常使用需要static final修饰ThreadLocal变量,这样导致ThreadLocal变量永远不会回收,破坏了弱引用的规则,弱引用失效! 必须手动清除!
为什么ThreadLocal实例除了添加static final 修饰之后,还常常加上了private修饰呢?
因为使用ThreadLocal会带来内存泄露的风险,所以需要避免ThreadLocal被外部类使用,可以提供封装的方法,供外部调用
如何回收弱引用?
JVM利用调用ThreadLocal的remove、get、set方法的时候,回收弱引用。
remove()
try {.........} finally {tl.remove();}

