ThreadLocal为什么要用弱引用和内存泄露问题

study 专栏收录该内容
13 篇文章 0 订阅

好早之前写的了🤣 ,没看懂🧐 ?传送门 👉 ThreadLocal详解

一.ThreadLocal

1.是什么:

ThreadLocal,可以理解为线程本地变量。它为T类型的变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,线程之间读写对象的操作是相互隔离、互不影响的。

2.实现思路:

先看一个定义:ThreadLocal类中,有一个静态内部类——ThreadLocalMap,虽然名字有Map,但并没有实现java的Map接口,其内部自定义的存储实体Entry类,继承了弱引用类WeakReference,以ThreadLocal的弱引用对象作为Entry的key,实际要存储的T类型对象,作为value。

Thread类中有一个类型为ThreadLocal.ThreadLocalMap的成员变量,名字叫threadLocals,默认为null,在进行get之前,必须先set,否则会报空指针异常。
如果不想set,则须重写initialValue方法。
当我们在程序中new一个ThreadLocal的对象local:

ThreadLocal<Object> local = new ThreadLocal<Object>() {
        @Override
        protected Object initialValue() {
                    return new Object(); 
        }
 };

调用local.set方法设值或者在get方法中执行initialValue方法初始化值后,当前线程的threadLocals中会有一个Entry,key弱引用指向对象local,value是Object对象,内存中的结构如下图:

之后,我们就能通过对象local的get方法,去访问存储在当前线程中独立的Object对象了,访问步骤:
(1)拿到当前线程,Thread t = Thread.currentThread();
(2)拿到线程中的成员变量threadLocals,ThreadLocalMap map = getMap(t);
(3)对map进行判断,不为null则以local为key,获取Entry对象e,e不为null则返回存储的Object对象。
(4)map为null,则执行setInitialValue方法,初始化map,返回Object对象。

最常见的ThreadLocal使用场景为:数据库连接管理、Session管理等。

二.ThreadLocal为什么要用弱引用

1.弱引用:

只具有弱引用的对象拥有短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

2.为什么用弱引用:

假如每个key都强引用指向ThreadLocal的对象,也就是上图虚线那里是个强引用,那么这个ThreadLocal对象就会因为和Entry对象存在强引用关联而无法被GC回收,造成内存泄漏,除非线程结束后,线程被回收了,map也跟着回收。

3.依然存在的内存泄漏问题:

当把ThreadLocal对象的引用置为null后,没有任何强引用指向内存中的ThreadLocal实例,threadLocals的key是它的弱引用,故它将会被GC回收。
但线程中threadLocals里的value却没有被回收,因为存在着一条从当前线程对象连接过来的强引用,且因为无法再通过ThreadLocal对象的get方法获取到这个value,它永远不会被访问到了,所以还存在内存泄漏问题。
同样的,只有在当前线程结束后,线程对象的引用不再存在于栈中,强引用断开,内存中的Current Thread、ThreadLocalMap、value才会全部被GC回收。

4.解决方案:

当线程的某个ThreadLocal对象使用完了,马上调用remove方法,删除Entry对象。
其实只要这个线程对象及时被GC回收,这个内存泄露问题影响不大,只发生在ThreadLocal对象的引用设为null到线程结束的这段时间内。
但在使用线程池的时候,线程结束是不会被销毁的,会再次使用,就可能出现真正的内存泄露。

补充:
Java为了最小化内存泄露的可能性和影响,在ThreadLocal的get、set的时候,都会检查当前key所指的对象是否为null,是则删除对应的value,让它能被GC回收。

  • 8
    点赞
  • 11
    评论
  • 19
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值