关于ThreadLocal的内存泄露

To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.为了应对超级大和长时间的用途,哈希表使用弱引用的 key。

作者对官方文档的理解有点问题,弱引用对象是entry而不是threadlocal,entry持有对threadlocal的弱引用,它继承了weakReference, 要了解entry为什么用弱引用,那就需要知道弱引用的好处,弱引用对象并非一发生gc就被收回,只有它的引用对象为null了的前提下(这里就是指threadlocal=null),entry对象的active状态才会转化,后进入一个队列,该队列在下次gc时,由gc线收回弱引用对象本身, 注意entry对象中的value依旧可能存在于堆。理解了以上知识,再来看真正的内存泄露是什么:

1. 如果使用了static修饰的threadlocal, 由于threadlocal是类的属性,只要类加载了后就一直存在,所以导致引用了该threadlocal的entry一直无法回收,这个时候就需要用到remove() 这一保底的动作,需要靠程序员手动清除value,所以一般不提议用静态threadlocal

2. 如果是用的非静态的threadlocal, 由于threadlocal是类实例的属性,如果实例被回收了,threadlocal最终也会被回收(如果不再有其他外部引用的情况下),而回收之前,即使开发人员粗心没有进行remove(),由于entry对threadlocal是弱引用且threadlocal已经为null, 这种情况还是可以回收避免内存泄露,如果用强引用就不行了

3. 再谈下对于value的疑惑,有人会疑惑entry本身是弱引用可以回收没错,value却不必定,由于即使entry被回收后为null了,value依然可能由于有其它外部强引用而存在于堆,没错,这种情况实则不能算是内存泄露了,是一种正常情景,实则value作为thread特有的副本资源,一般尽量单独创建给当前线程使用为佳,避免外部引用,这样在最坏的情况下,即使开发人员使用后忘记remove, value也可以在entry被回收后的某个时间点被gc回收, 由于这时它在堆上已处于一种不可达状态。

4. 使用范式必定要遵从

try{

threadlocal.get();

//TODO业务逻辑

}finally{

threadlocal.remove();

}

不然可能会由于get的后续业务逻辑报错,请求在remove前就终止了,这样下次的请求从线程池如果拿到一样的线程,很可能会导致严重的业务错误。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容