ThreadLocal内存泄漏的原因,如何避免
什么是内存泄漏
不再会被使用的对象或者变量占用的内存不能够被回收就是内存泄漏
内存泄漏是在程序申请内存后,无法释放已申请的内存空间,一次内存泄漏危害可以忽略,但内存泄漏的后果严重,无论多少内存迟早会被占光。
强引用和弱引用
-
强引用:使用最普遍的引用(new),一个对象具有强引用,不会被垃圾回收器回收。当内存不足时,JVM宁愿抛出OutOfMemoryError错误,是程序异常终止,也不会回收具有强引用的对象
如果想取消强引用和某个对象之间的关联,可以显示的将引用赋值为null,这样可以使JVM在合适的时间回收该对象 -
弱引用:JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在Java中,用java.lang.ref.WeakReference类来表示。可以在缓存中使用弱引用。
ThreadLocal的实现原理
每一个Thread维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal不存在外部强引用,key(ThreadLocal)势必会被垃圾回收器回收,这样就会导致ThreadLocalMap中的key为null,而value还存在着强引用,只有thread线程退出以后,value的强引用链条才会断掉,但如果当前线程迟迟不结束的话,这些key为空的Entry的value就会一直存在强引用链(红色链)不能够被回收。
两种假设:
- key使用强引用
当ThreadLocalMap的key为强引用回收ThreadLocal时,因为ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。 - key使用弱引用
当ThreadLocalMap的key为弱引用回收ThreadLocal时,由于ThreadLocalMap持有的是ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。当key为空时,在下一次ThreadLocalMap调用get()、set()、remove()方法的时候也会被清除。
内存泄漏的根源:
由于ThreadLocalMap的生命周期和Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
ThreadLocal的正确使用方法:
- 每次使用完ThreadLocal都调用它的remove()方法清除数据
- 将ThreadLocal变量定义成private static,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉。