针对 ThreadLocal 出现内存泄露的原因分析

137 阅读1分钟

出现内存泄露的原因:

Entry(ThreadLocal<?> k, Object v) {

super(k);//key 是弱引用,

value = v;//强引用

}

GC 只会清掉弱引用指向的对象,不会自动把 Entry 整个移除

  • Entry 本身是一个普通对象,它还被 ThreadLocalMap.table[] 的数组强引用着。
  • 也就是说,即使 key 被 GC 回收(变成 null),这个 Entry 还会留在数组里,value 依旧强引用着你的对象,不会自动释放。
  • 这就是内存泄漏的风险所在。

ThreadLocalMap 被动清理:

(1)get 时的清理:在 getEntry 查找时,如果通过 hash 定位没找到,会触发线性探测查找其他位置。

在遍历过程中,如果发现某个 Entry.key == null(说明 ThreadLocal 已被 GC 回收),就会调用 expungeStaleEntry 清理这个 entry,把 value 也置为 null,并调整数组结构。

(2)set 时的清理

在 set 时会先用 hash 找位置:如果该位置是空的,直接插入。如果该位置 key 相同(==),更新 value。

如果不同,就向后探测直到找到空位或者相同 key。在探测过程中,如果发现 key 为 null(脏 entry),会调用 replaceStaleEntry 来替换,并顺便清理。

ThraedLocalMap 主动清理:

1.在资源不再使用的时候,会调用 Remove 进行移除。

类图:

image.png

因为 entry 属于弱引用的类,在 GC 的时候被回收,会导致 key丢失。如果在 ThreadLocal 销毁时,并没有进行对应 value 的清除操作。就会导致 value 存储在数组Entry[]中;加上Thread 持有ThreadLocalMap对象。在可达性算法检测下,存在引用链。就无法正常回收。因此就会导致内存泄露出现。