天天看点

关于ThreadLocal内存泄漏的问题

什么是ThreadLocal?

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

  • 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
  • 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。

以上就不多说了,百度一下多的是这个描述

怎么使用ThreadLocal?

public static void main(String[] args) { 

ThreadLocal local = new ThreadLocal();
 local.set("fafdasfsadf");//设置值

}
           

分析源代码来帮助我们了解

我们来看一下ThreadLocal的set方法

ThreadLocal.java
-----------------------------------------------------------------------
 public void set(T value) {
        Thread t = Thread.currentThread(); //获取当前线程
        ThreadLocalMap map = getMap(t); //从当前线程里面获取threadLocalMap
        if (map != null)    
            // this是当前ThreadLocal对象作为key, value是我们传来的值
            map.set(this, value);
        else
            createMap(t, value);
    }


//getMap(t) t是当前线程
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals; // 返回当前线程的局部变量 threadLocals
    }


Thread.java
----------------------------------------------------------------------
    //Thread里面定义了一个变量名称:threadLocals 类型为 ThreadLocalMap 
    ThreadLocal.ThreadLocalMap threadLocals = null;
           

梳理一下逻辑

1. 当前ThreadLocal对象作为key值

2.保存到我们的当前线程的一个threadLocals的局部变量里面

3.这个threadLocals变量其实是一个ThreadLocal里面定义的一个静态的内部类:ThreadLocalMap

4.这个map的key是弱引用

分析

ThreadLocal local = new ThreadLocal();      
local.set("fafdasfsadf");       

当执行set的时候

1.获取当前运行的线程t

2.拿到当前线程t的局部变量threadLocals(其实就是一个map形式的内部类)

3.把ThreadLocal对象作为key值 保存到当前线程t的局部变量里面

从下图可以清晰的看见 [email protected]对象作为key值( 弱引用 referent)  保存到了我们线程t 的threadLocals局部变量中

 这三者的关系就是这样的,看下图应该就很清楚了,一目了然,有没有,嘻嘻

关于ThreadLocal内存泄漏的问题

关于弱引用

弱引用的特点是,如果这个对象只存在弱引用,那么发生GC的话就会被回收

local 是栈帧里面的局部变量 指向的堆中[email protected] 的引用地址,所以这里还是强引用

ThreadLocal local = new ThreadLocal();
      

我们要把local指向的引用设置null ,就会引发下面的结果

1这下的话 ThreadLocalMap中 key为:[email protected] 就只存在弱引用

2.当发生GC的时候,就会被垃圾收集器回收

3.回收过后该map的key就会变成null

4.看下图可以发现value并没有回收,就出现了key值为null 的数据

如下图 :

关于ThreadLocal内存泄漏的问题

关于内存泄漏

内存泄露就是堆内存中不再使用的对象,但是垃圾回收期无法及时的回收(从内存中删除他们的情况),因此他们会被不必要的一直存在,导致内存的浪费。

关于上面弱引用对象被回收,key值为null的情况  -  ThreadLocalMap实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。

我认为这里的内存泄露是因为ThreadLocalMap随Thread存在而存在,如果只是普通的使用单线程的情况是正常的;

但是如果在线程池的情况下,线程是会被复用的,如果我们不及时remove()的话,就会出现之前定义的ThreadLocalsmap一直都存在,就会造成内存泄漏

建议回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄露等问题

关于上面的key值名称是referent

因为ThreadLocalMap是Thread自定义的Map

key是弱引用,所以上面key值的名称是referent

关于ThreadLocal内存泄漏的问题
关于ThreadLocal内存泄漏的问题