这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战
在分析ThradLocal方法的时候,我们了解到ThreadLocal的操作实际上是围绕ThreadLocalMap展开的。ThreadLocalMap的源码相对比较复杂。
基本结构
ThreadLocalMap是ThreadLocal的内部类,没有实现map接口,用独立的方式实现了map的功能,其内部结构Entry也是独立实现的。
ThradLocalMap内部结构
/**
* 初始容量,必须是2的整次幂
*/
private static final int INITIAL_CAPACITY = 16;
/**
* 存放数据的table,Entry类的定义在下面分析
* 同样,数组长度必须是2的整次幂
*/
private Entry[] table;
/**
* 数组里面的entry的个数,可以用于判断table当前使用量是否超过阈值
*/
private int size = 0;
/**
* 进行扩容的阈值,表使用量大于它的时候进行扩容
*/
private int threshold; // Default to 0
复制代码
跟HashMap类似,INITIAL_CAPACITY代表初始容量;table是一个Entry类型的数组,用于存储数据;size代表 表中的存储数目; threshold代表需要扩容时对于size的阈值。
存储结构-Entry
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
复制代码
在ThreadLocalMap中,也是用Entry来保存Key-Value的,不过Entry中的key只能是ThreadLocal对象,这点在构造方法中已经限定死了。
另外,Entry继承自WeakReference,也就是key-ThreadLocal是弱引用,其目的是将ThreadLocal对象的生命周期和线程生命周期解绑。
弱应用和内存泄漏
- 内存泄露相关概念
- Memory overflow: 内存溢出,没有足够的内存提供申请者使用。
- Memory leak: 内存泄漏是指程序中已动态分配的堆内存由于某种原因,程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统奔溃等严重后果。内存泄漏的堆积终将导致内存溢出。
- 弱引用相关概念
Java中的引用有4种类型:强软弱虚。当前这个问题主要设计到强引用和弱引用;
强引用(Strong Reference):就是我们最常见的普通对象引用,只要还有强引用只想一个对象,救恩那个表明对象还活着,垃圾回收器就不会回收这种对象。
**弱引用(WeakReference)**垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
- 如果key使用强引用
假设ThreadLocalMap中的key使用了强引用,那么会出现内存泄漏?
此时ThreadLocal的内存图如下:
- 假设我们的代码中用完了ThreadLocal,threadlocal Ref被回收了。
- 但是因为threadlocalMap的Entry强引用了threadLocal,造成threadLocal无法被回收。
- 在没有手动调用remove删除这个Entry以及CurrentThread依然运行的前提下,始终有强引用链threadRef->currentThread->threadLocalMap->entry,Entry就不会被回收,导致Entry内存泄漏
也就是说,ThreadLocalMap中的Key使用了强引用,是无法完全避免内存泄漏的
- 如果key使用弱引用
- 出现内存泄漏的真实原因