目录
1.内存泄漏
- 内存溢出: Memory overflow 没有足够的内存提供申请者使用.
- 内存泄漏: Memory Leak 程序中已经动态分配的堆内存由于某种原因, 程序未释放或者无法释放, 造成系统内部的浪费, 导致程序运行速度减缓甚至系统崩溃等严重结果. 内存泄漏的堆积终将导致内存溢出
源码:ThreadLocal - 基础_java threadlocal创建数据库-优快云博客
2.假设堆中的ThreadLocal对象被Entry强引用
- 假设在业务代码中使用完ThreadLocal, ThreadLocal ref被回收了
- 但是因为threadLocalMap的Entry强引用了threadLocal(key就是threadLocal), 造成ThreadLocal无法被回收
- 在没有手动删除Entry以及CurrentThread(当前线程)依然运行的前提下, 始终有强引用链CurrentThread Ref → CurrentThread →Map(ThreadLocalMap)-> entry, Entry就不会被回收( Entry中包括了ThreadLocal实例和value), 导致Entry内存泄漏
3. 实际上堆中的ThreadLocal对象被Entry弱引用
- 假设在业务代码中使用完ThreadLocal, ThreadLocal ref被回收了
- 由于threadLocalMap只持有ThreadLocal的弱引用, 没有任何强引用指向threadlocal实例(这里Entry不再强引用ThreadLocal了), 所以threadlocal就可以顺利被gc回收, 此时Entry中的key = null
- 在没有手动删除Entry以及CurrentThread依然运行的前提下, 也存在始终有强引用链CurrentThread Ref → CurrentThread →Map(ThreadLocalMap)-> entry,value就不会被回收, 而这块value永远不会被访问到了(因为key=null), 导致value内存泄漏。
4. 内存泄漏的真实原因
比较以上两种情况,我们就会发现:
内存泄漏的发生跟 ThreadLocalIMap 中的 key 是否被弱引用是没有关系的。
ThreadLocal 内存泄漏的根因是:
- 由于ThreadLocalMap 的生命周期跟当前 Thread 一样长,在当前Thread结束之前,如果没有手动删除对应的Entry(手动调用remove()方法)就会导致内存泄漏。
要避免内存泄漏有两种方式:
- 使用完 ThreadLocal ,调用其 remove 方法删除对应的 Entry
- 使用完 ThreadLocal ,当前 Thread 也随之运行结束
相对第一种方式,第二种方式显然更不好控制,特别是使用线程池的时候,线程结束是不会销毁的。也就是说,只要记得在使用完ThreadLocal 及时的调用 remove ,无论 key 是强引用还是弱引用都不会有问题。
5. 为什么key要被弱引用
- 事实上,在 ThreadLocalMap 中的set/getEntry 方法中,会对 key 为 null(也即是 ThreadLocal 为 null )进行判断,如果为 null 的话,那么会把 value 置为 null 的。
- 这就意味着使用完 ThreadLocal , 在CurrentThread 依然运行的前提下,如果忘记手动调用 remove 方法,那么被弱引用会比被强引用多一层保障:被弱引用的 ThreadLocal 会被回收,对应value在下一次 ThreadLocaIMap 调用 set/get/remove 中的任一方法的时候会被清除,从而避免内存泄漏。