关于ThreadLocal内存泄露


关于ThreadLocal内存泄露

ThreadLocal、ThreadLocalMap、Thread三者基本关系

【问题1】为什么key(ThreadLocal)定义为弱引用

  • 方便回收value值
  • 如果key为强引用,那么ThreadLocal即使被手动置空gc也依然不能将其回收,而ThreadLocal无法被回收,就导致在Thread生命周期结束之前,明明对应的key已经没用了,也将其显示置空了,但gc却依然无法回收这部分的空间,这就导致了内存泄露问题
  • 所以有些人说是弱引用导致的内存泄露,这显然是不正确的,弱引用反而降低了内存泄露的可能,因为在ThreadLocalMap的set和get方法中都带有一定的清理能力(针对清理key为null的entry,即ThreadLocal被回收后的处理)

【问题2】弱引用有利于空间的回收,那为什么依然存在内存泄露问题?

  • 虽然ThreadLocalMap在自己的set、get方法中添加了不少清理key为null的entry的操作,但是其处理能力不能保证所有key为null的entry都被清理掉,所以导致即使ThreadLocal被置空都无法回收对应的value值
  • 【注意】这里需要提一下的是,什么情况下value才会被gc回收?通过源码的查看,发现只有ThreadLocalMap发现key为null时才会将value的引用置空。因为在Entry中value是强引用,所以ThreadLocalMap不主动置空value引用的话,gc是无法在Thread生命周期没有结束之前的这段时间将其回收的。
  • 所以为到底什么导致内存泄露呢?答案就是ThreadLocal置空后没有被ThreadLocalMap发现,导致ThreadLocalMap不能主动将value置空,从而造成gc无法回收value的空间。

【问题3】如果解决ThreadLocal的内存泄露问题?

  • 虽然我们知道ThreadLocalMap的set和get有清理key为null的entry的能力但是并非百分百管用。所以提倡用完ThreadLocal之后,主动调用ThreadLocal的remove方法来释放value的内存。
  • 【注意】除此之外,我看到有人建议将ThreadLocal修饰成static。大概的思想是提升ThreadLocal的生命周期范围。但事实上经过上面的分析,这种操作对内存泄露问题没有半点帮助。为什么呢?因为不管ThreadLocal的生命周期有多广,请不要忘记ThreadLocal本质上是一个线程变量,即同一个ThreadLocal对象在不同的Thread中都是不同的。通俗点说,如果线程生命周期还没结束,而用完ThreadLocal后又没有主动释放value的内存,就会造成内存泄露。当然Thread结束内存就被释放了。但现在讨论的是线程还没结束而导致的内存泄露问题,因此还是建议养成良好的习惯,用完就使用remove释放value内存


分享到:


相關文章: