ThreadLocal实现与内存泄漏

ThreadLocal实现在网上一抓一大把,就不粘贴了。之前使用的时候一直没注意过也没使用过remove()这个方法,最近才注意到可能会导致内存泄漏的问题,由此去google了一下然后看了一下ThreadLocal的源码,看了一些分析内存泄漏的文章感觉都是粘贴的,没有根据代码进行深入的分析,所以谈一下我对ThreadLocal的内存泄漏的理解。

实现概览

ThreadLocal在内部实现是通过当前线程的的ThreadLocalMap维护的,它是放在Thread实例中的,每一个线程都有这个属性。在第一次调用ThreadLocal的set或者get方法的时候会实例化它,ThreadLocalMap内部真正维护的是一个Entry数组,它的实现很像HashMap,不过也有它自己的特点(后面会详细说这个弱引用以及由此产生的内存泄漏分析)Entry的key就是ThreadLocal实例,value就是我们向ThreadLocal存放的值。

内存泄漏

内存泄漏的核心产生的点就是在Thread实例中的ThreadLocalMap对象,如果Thread对象只是简单的使用完毕销毁当然就没有这个问题了,很多时候都是使用的线程池,一直复用之前的线程对象,那么ThreadLocalMap对象就永远不能释放了,同时它持有的Entry数组中引用的对象,ThreadLocal实例作为key的引用是一个弱引用(可以理解为当对象只被弱引用持有时会在下次gc清理掉),假如ThreadLocal实例没有被其他对象应用,那么也就有可能产生key被回收了,value还在Entry对象中引用,那么就永远回收不了了。这么看就造成的内存泄漏,这也是网上搜出来最多的理解,这里从代码层面描述。

补救

最直接的方法就是在使用完毕后直接调用remove()方法,在Entry数组中移除。
其次当我们使用static final关键字将ThreadLocal对象声明成类属性时,其实就永远不可能因为弱引用被移除了。在大多数情况下这样使用是没有问题的。但是极少数情况会导致严重的内存泄漏问题。尤其是当和类加载器机制在一起时,问题就被放大了。见下面的链接
深入分析 ThreadLocal 内存泄漏问题
ThreadLocal 内存泄露的实例分析

总结

最佳实践还是调用remove方法,毕竟线程持有时拉长了整个对象的生命周期,你不知道什么时候会被回收,这是一个危险的事情。

你可能感兴趣的:(ThreadLocal实现与内存泄漏)