ThreadLocal源码分析、使用场景、内存泄漏(一)

    1、ThreadLocal线程的局部变量。既然是线程局部变量,那么理所当然就应该存储在自己的线程对象中,我们可以从 Thread 的源码中找到线程局部变量存储的地方:

ThreadLocal源码分析、使用场景、内存泄漏(一)_第1张图片

我们可以看到线程局部变量是存储在Thread对象的 threadLocals 属性中,而 threadLocals 属性是一个 ThreadLocal.ThreadLocalMap 对象。

2、ThreadLocalMap是什么呢?

ThreadLocal源码分析、使用场景、内存泄漏(一)_第2张图片

可以看到他是ThreadLocal的一个静态内部类,内部是一个自己实现的以ThreadLocal为key值的map,使用了弱引用的方式。方便内存的回收。

ThreadLocal源码分析、使用场景、内存泄漏(一)_第3张图片

从构造函数中可以看出,他是一个 private Entry[] table数组,通过firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)实现key到value的映射,初始化大小为16。

ThreadLocal源码分析、使用场景、内存泄漏(一)_第4张图片

threadLocalHashCode 是一个原子变量每次增加0x61c88647实现,然后 & (INITIAL_CAPACITY - 1) 取得在数组 private Entry[] table 中的索引。

3、ThreadLocal初始化

ThreadLocal源码分析、使用场景、内存泄漏(一)_第5张图片
ThreadLocal源码分析、使用场景、内存泄漏(一)_第6张图片

首先在当前线程中获取threadLoals,如果为null,则进行初始化工作。

ThreadLocal源码分析、使用场景、内存泄漏(一)_第7张图片
ThreadLocal源码分析、使用场景、内存泄漏(一)_第8张图片

4、内存的回收

ThreadLocal 涉及到的两个层面的内存自动回收

    当线程死亡时,那么所有的保存在的线程局部变量就会被回收,其实这里是指线程Thread对象中的 ThreadLocal.ThreadLocalMap threadLocals 会被回收,这是显然的。

如果线程可以活很长的时间,并且该线程保存的线程局部变量有很多(也就是 Entry 对象很多),那么就涉及到在线程的生命期内如何回收 ThreadLocalMap 的内存了,不然的话,Entry对象越多,那么ThreadLocalMap 就会越来越大,占用的内存就会越来越多,所以对于已经不需要了的线程局部变量,就应该清理掉其对应的Entry对象。使用的方式是,Entry对象的key是WeakReference 的包装,当ThreadLocalMap 的 private Entry[] table,已经被占用达到了三分之二时 threshold = 2/3(也就是线程拥有的局部变量超过了10个) ,就会尝试回收 Entry 对象,我们可以看到 ThreadLocalMap.set方法中有下面的代码:

if (!cleanSomeSlots(i, sz) && sz >= threshold)

    rehash();

5、 ThreadLocal常用的接口:

1)需要制定初始值时,可以覆盖protected T initialValue()方法;

2)public T get();

3)public void set(T value);

4)public void remove();

6、总结

1)一个线程中的所有的局部变量其实存储在该线程自己的同一个map属性中;

2)线程死亡时,线程局部变量会自动回收内存;

3)线程局部变量时通过一个 Entry 保存在map中,该Entry 的key是一个 WeakReference包装的ThreadLocal, value为线程局部变量; 

     key 到 value 的映射是通过:ThreadLocal.threadLocalHashCode & (INITIAL_CAPACITY - 1) 来完成的;

4)当线程拥有的局部变量超过了容量的2/3(没有扩大容量时是10个),会涉及到ThreadLocalMap中Entry的回收;

参考文章:https://www.cnblogs.com/digdeep/p/4510875.html

你可能感兴趣的:(ThreadLocal源码分析、使用场景、内存泄漏(一))