不会再被使用的对象或者变量占用的内存不能被回收,就是内存泄露。
强引用:使用最普遍的引用(New),一个对象具有强引用将不会被GC回收。当内存空间不足时,jvm宁愿抛出OutOfMemoryError错误使程序终止,也不回收这种对象。
弱引用:使用java.lang.ref.WeakReference表示。jvm在进行垃圾回收时,无论内存是否充足,都会回收这种对象。
ThreadLocal的实现原理:每一个Thread维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本。
ThreadLocal在没有外部强引用时,发生GC时会被回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。
假如hreadLocalMap的key为强引用,回收ThreadLocal时,因为ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,因而value也不会被回收,导致Entry内存泄漏。
让key(threadLocal对象)为弱引用,自动被垃圾回收,key就变为null了,下次,我们就可以通过Entry不为null,而key为null来判断该Entry对象该被清理掉了。
Entry的key被设计为弱引用就是为了让程序自动地对访问不到的数据进行回收,所以,在访问不到的数据被回收之前,内存泄漏确实是存在的,但是我们不用担心,就算我们不调用remove,ThreadLocalMap在内部的set,get和扩容时都会清理掉泄漏的Entry,内存泄漏完全没必要过于担心
因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
每次使用完ThreadLocal都调用它的remove()方法清除数据
将ThreadLocal变量定义成private static,设计为static的,被class对象给强引用,线程存活期间就不会被回收。这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉 。
参考链接:
《提升能力,涨薪可待》-ThreadLocal的内存泄露的原因分析以及如何避免 - 掘金
Java中ThreadLocal的实际用途是啥? - 知乎
谈谈ThreadLocal为什么被设计为弱引用 - 知乎