ThreadLocal内存泄漏原因

  • weakReference和threadLocal的基本用法

    必须放在这里,因为后面好用于解析源码的时候回忆。

//weak ref
WeakReference<Activity> ref = new WeakReference<>(activity);

//thread local
ThreadLocal<String> local = new ThreadLocal<>();
local.set("test");
String s = local.get();
local.remove();
  • 代码结构

在这里插入图片描述

画图结合使用逻辑,就清楚了。

假设我们自己的代码中local已经没有引用了,比如Test已经置为null,或者local置为null,可达性已经失去;

那么,作为Entry的key就被回收;而value是强引用设置的值,如果是复杂类型数据的话,就会长时间留存在ThreadLocalMap(table)数组中。在某些get(), remove() , resize() 扩容,set()动作的时候,会尝试clear部分key为null的entry,移除数组并标记value=null,去除引用。

但是这些动作并不一定能跑进去。而且假设你申请的local并不是特别频繁使用的话(即很少调用set,remove, get),这个value的引用将会存在无法回收。

解决方案:用完记得remove掉。比如Thread结束的时候,或者数据使用完后,记得remove()。有的帖子说搞成static,下面我将Handler会提到。

  • 扩展点1:多线程下有几个local?几个map?多个local变量申明呢?

    • 假设1个local变量,多线程:

      这个很容易理解了吧,每个Thread下面都挂着一个map,只是我们这一个local被多次拿去做Entry key了,保存着不同的value。

      Android Handler就是这个模式。

    • 假设多个local变量,单线程:

在这里插入图片描述

 如灵魂画手,即,该线程下的map的数组不再是单个Entry元素了。而是多个k v组成。
  • 假设多个local变量,多线程:

    这下好理解了,不用多说。每一个线程都有map,map下的数组就对应有多个Entry(local组成弱引用的key,和value)。

  • 扩展点2 Android Handler

    ​ 就是把Looper对象,申明了一个static的LocalThread在我们prepare的时候,进行初始化。这样的情况,就保证了每一个线程,在new出来的时候,都会创建一个独有的Looper。

    ​ 所以说,ThreadLocal和Thread有什么关系呢?Thread只和ThreadLocalMap有关,而我们申明的变量,ThreadLocal是作为ThreadLocalMap的数组中一个元素的key(weakRef)。

​ 那为什么在Handler和Looper源码中看不到remove来解决内存泄漏呢?

​ 这里他使用static来标记threadLocal变量。即保证了所有线程只有一个key。这个key永远不会被回收了。则不会出现key=null,value存在的现象。但是假设HandlerThread被回收,Looper作为value会被先回收。理论上讲,他并没有考虑内存问题。如果线程和Looper搞的多,在前面提到的逻辑中,靠的ThreadLocal内部代码的比如set,或者resize的时候,进行清理(详细可以阅读ThreadLocal.java:set()->cleanSomeSlots,即每次设置值都会尝试清理清理)。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值