1.ThreadLocal源码
//ThreadLocal.ThreadLocalMap 类
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry继承自弱引用,以保证只要发生GC就会回收掉引用对象。
这里经常有一个疑惑,回收的是Entry对象还是ThreadLocal对象。从理论上来说,Entry对象本身是强引用,使用弱引用的目的就是为了把WeakReference它所持有的泛型类型对象及时回收,所以回收的是ThreadLocal对象;
(Entry为什么使用弱引用?)
2.先搞清楚弱引用回收的是什么?
查看弱引用源码:
public class WeakReference<T> extends Reference<T> {
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
//直接看Reference类
public abstract class Reference<T> {
//引用对象
volatile T referent;
//通过构造函数给引用对象赋值
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = queue;
}
//通过native方法获取该引用对象
@FastNative
private final native T getReferent();
//通过native方法自动回收引用对象
@FastNative
native void clearReferent();
//提供给java调用的主动回收对象的方法
public void clear() {
clearReferent();
}
}
(demo测试:)
ThreadLocal<String> a = new ThreadLocal<>();
ThreadLocal<String> b = new ThreadLocal<>();
Entry[] f = new Entry[2];
private void testgc(){
f[0] = new Entry(a, "aaa");
f[1] = new Entry(b, "bbb");
for (Entry entry : f) {
System.out.println(entry);
}
a = null;
Log.d(TAG, "start gc");
System.gc();
Log.d(TAG, "after gc");
for (Entry entry : f) {
Log.d(TAG, "print entry:" + entry);
}
new Handler(getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
Log.d(TAG, "gc时机不确定,1秒后保证gc发生之后再打印:");
for (Entry entry : f) {
Log.d(TAG, "entry:" + entry);
}
}
}, 1000);
}
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
public Entry(ThreadLocal<?> threadLocal, Object value) {
super(threadLocal);
this.value = value;
}
@Override
public String toString() {
return "Entry{" +
"value=" + value + "\n" +
"threadlocal=" + this.get() +
'}';
}
}
//log
MainActivity: start gc
MainActivity: after gc
MainActivity: print entry:Entry{value=aaa threadlocal=java.lang.ThreadLocal@d997cd4}
MainActivity: print entry:Entry{value=bbb threadlocal=java.lang.ThreadLocal@e5aee7d}
MainActivity: gc时机不确定,1秒后保证gc发生之后再打印:
MainActivity: entry:Entry{value=aaa threadlocal=null}
MainActivity: entry:Entry{value=bbb threadlocal=java.lang.ThreadLocal@e5aee7d}
//结果显示:gc之后,f[0]对象仍存在,而threadlocal为null,
//说明只有内部的threadlocal被回收了
进一步说明弱引用回收的就是泛型类型对象;
3. “引用” 和 “对象” 的概念,以及它们和内存泄漏的关系
**引用:**存储在方法区的变量池、运行时数据区的虚拟机栈、或堆对象中的引用句柄,引用是运行时符号,指向的是对象在堆内存中的地址;
**对象:**分配在堆内存中的实例数据;
内存泄漏是如何发生的?:通过可达性分析之后,没有GCRoots指向的对象会被回收,那么当一个对象在业务中不再使用需要释放却还有GCRoots引用指向的时候就发生了内存泄漏。
4.ThreadLocal 泄漏分析
(通过源码查看泄漏位置:)
/**
* Remove the entry for key.
*/
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) { //这里e.get()获取ThreadLocal为null
e.clear();//清空弱引用
expungeStaleEntry(i); //清空Enty和其中的value
return;
}
}
}
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
//......
}