Java四大引用
- 強(qiáng)引用:絕不回收
- 軟引用:內(nèi)存不足才回收
- 弱引用:碰到就回收
- 虛引用:等價(jià)于沒有引用,只是用來標(biāo)識(shí)下指向的對(duì)象是否被回收。
WeakReference類
弱引用, 當(dāng)一個(gè)對(duì)象僅僅被weak reference(弱引用)指向, 而沒有任何其他strong reference(強(qiáng)引用)指向的時(shí)候, 如果這時(shí)GC運(yùn)行, 那么這個(gè)對(duì)象就會(huì)被回收,不論當(dāng)前的內(nèi)存空間是否足夠,這個(gè)對(duì)象都會(huì)被回收
WeakReference繼承Reference,其中只有兩個(gè)構(gòu)造函數(shù):
/**
* Creates a new weak reference that refers to the given object. The new
* reference is not registered with any queue.
*
* @param referent object the new weak reference will refer to
*/
public WeakReference(T referent) {
super(referent);
}
/**
* Creates a new weak reference that refers to the given object and is
* registered with the given queue.
*
* @param referent object the new weak reference will refer to
* @param q the queue with which the reference is to be registered,
* or <tt>null</tt> if registration is not required
*/
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
- referent:被弱引用的對(duì)象,
- ReferenceQueue:應(yīng)用隊(duì)列,在對(duì)象被回收后,會(huì)把弱引用對(duì)象,也就是WeakReference對(duì)象或者其子類的對(duì)象,放入隊(duì)列ReferenceQueue中,注意不是被弱引用的對(duì)象,被弱引用的對(duì)象已經(jīng)被回收了。
private void test() {
// 創(chuàng)建一個(gè)對(duì)象(強(qiáng)引用)
Object obj = new Object();
// 創(chuàng)建一個(gè)弱引用,并指向這個(gè)對(duì)象,并且將引用隊(duì)列傳遞給弱引用
WeakReference<Object> reference = new WeakReference(obj, queue);
// gc一次看看
System.gc();
此時(shí)循環(huán)打印引用隊(duì)列為null
while ((obj = queue.poll()) != null) {
System.out.println(": " + obj);
}
// 設(shè)置obj為null,現(xiàn)在只有弱引用引用,可以被回收了
obj = null;
// 再進(jìn)行g(shù)c,此時(shí)obj應(yīng)該被回收了,那么queue里面應(yīng)該有這個(gè)弱引用了
System.gc();
// 再打印隊(duì)列不為
Object obj;
while ((obj = queue.poll()) != null) {
System.out.println(": " + obj);
}
}
LeakCanary工作原理
利用弱引用特性,檢測(cè)Activity 的內(nèi)存泄漏
LeakCanary.install(application);此時(shí)使用application進(jìn)行registerActivityLifecycleCallbacks,從而來監(jiān)聽Activity的何時(shí)被destroy。
在onActivityDestroyed(Activity activity)的回調(diào)中, 使用一個(gè)弱引用WeakReference指向這個(gè)activity,并且給這個(gè)弱引用指定一個(gè)引用隊(duì)列queue,同時(shí)創(chuàng)建一個(gè)key來標(biāo)識(shí)該activity。
然后將檢測(cè)的方法ensureGone()投遞到空閑消息隊(duì)列。
當(dāng)空閑消息執(zhí)行的時(shí)候,去檢測(cè)queue里面是否存在剛剛的弱引用,如果存在,則說明此activity已經(jīng)被回收,就移除對(duì)應(yīng)的key,沒有內(nèi)存泄漏發(fā)生。
如果queue里不存在剛剛的弱引用,則手動(dòng)進(jìn)行一次gc。
gc之后再次檢測(cè)queue里面是否存在剛剛的弱引用,如果不存在,則說明此activity還沒有被回收,此時(shí)已經(jīng)發(fā)生了內(nèi)存泄漏,直接dump堆棧信息并打印日志,否則沒有發(fā)生內(nèi)存泄漏,流程結(jié)束。
空閑消息被執(zhí)行的時(shí)候,大概率已經(jīng)發(fā)生過gc,所以可以檢測(cè)下gc后activity是否被回收。但是也可能還沒發(fā)生gc,那么此時(shí)activity沒有被回收是正常的,所以我們手動(dòng)再gc一下,確保發(fā)生了gc,再去檢測(cè)activity是否被回收,從而100%的確定是否發(fā)生了內(nèi)存泄漏。