一. java 的幾種 reference:
1咐刨,強(qiáng)引用(Strong Reference, 沒(méi)有具體的類來(lái)標(biāo)識(shí)強(qiáng)引用码俩,正常的使用的對(duì)象引用都是強(qiáng)引用度帮,由vm實(shí)現(xiàn))
強(qiáng)引用是使用最普遍的引用。如果一個(gè)對(duì)象具有強(qiáng)引用握玛,那垃圾回收器絕不會(huì)回收它够傍。
當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤挠铲,使程序異常終止冕屯,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足的問(wèn)題。
2拂苹,軟引用(SoftReference)
如果一個(gè)對(duì)象只具有軟引用安聘,則內(nèi)存空間足夠,垃圾回收器就不會(huì)回收它瓢棒;如果內(nèi)存空間不足了浴韭,就會(huì)回收這些對(duì)象的內(nèi)存。
只要垃圾回收器沒(méi)有回收它脯宿,該對(duì)象就可以被程序使用念颈。軟引用可用來(lái)實(shí)現(xiàn)內(nèi)存敏感的高速緩存。
軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用连霉,如果軟引用所引用的對(duì)象被垃圾回收器回收榴芳,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中嗡靡。
3,弱引用(WeakReference)
弱引用與軟引用的區(qū)別在于:只具有弱引用的對(duì)象擁有更短暫的生命周期。
在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過(guò)程中窟感,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象讨彼,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存柿祈。
不過(guò)哈误,由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對(duì)象躏嚎。
弱引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用蜜自,如果弱引用所引用的對(duì)象被垃圾回收,Java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用隊(duì)列中卢佣。
4袁辈,虛引用(PhantomReference)
“虛引用”顧名思義,就是形同虛設(shè)珠漂,與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期尾膊。如果一個(gè)對(duì)象僅持有虛引用媳危,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收冈敛。
虛引用主要用來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng)待笑。虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列 (ReferenceQueue)聯(lián)合使用。
當(dāng)垃圾回收器準(zhǔn)備回收一個(gè)對(duì)象時(shí)抓谴,如果發(fā)現(xiàn)它還有虛引用暮蹂,就會(huì)在回收對(duì)象的內(nèi)存之前,把這個(gè)虛引用加入到與之 關(guān)聯(lián)的引用隊(duì)列中癌压。
二.leakcanary原理
LeakCanary 中的 RefWatcher 就是通過(guò)弱引用及其隊(duì)列來(lái)實(shí)現(xiàn)監(jiān)控的:
有兩個(gè)很重要的結(jié)構(gòu): retainedKeys 和 queue 仰泻,
retainedKeys 代表沒(méi)被gc 回收的對(duì)象,
而queue中的弱引用代表的是被gc了的對(duì)象滩届,通過(guò)這兩個(gè)結(jié)構(gòu)就可以監(jiān)控對(duì)象是不是被回收了集侯;
retainedKeys存放了RefWatcher為每個(gè)被監(jiān)控的對(duì)象生成的唯一key;
同時(shí)每個(gè)被監(jiān)控對(duì)象的弱引用(KeyedWeakReference)關(guān)聯(lián)了 其對(duì)應(yīng)的key 和 queue帜消,這樣對(duì)象若被回收棠枉,則其對(duì)應(yīng)的弱引用會(huì)被入隊(duì)到queue中;
removeWeaklyReachableReferences(..)所做的就是把存在與queue中的弱引用的key 從 retainedKeys 中刪除泡挺。
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
/**
- Watches the provided references and checks if it can be GCed. This method is non blocking,
- the check is done on the {@link Executor} this {@link RefWatcher} has been constructed with.
- @param referenceName An logical identifier for the watched object.
*/
public void watch(Object watchedReference, String referenceName) {
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
if (debuggerControl.isDebuggerAttached()) {
return;
}
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
watchExecutor.execute(new Runnable() {
@Override public void run() {
ensureGone(reference, watchStartNanoTime);
}
});
}
void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();
if (gone(reference) || debuggerControl.isDebuggerAttached()) {
return;
}
gcTrigger.runGc();
removeWeaklyReachableReferences();
if (!gone(reference)) {
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == HeapDumper.NO_DUMP) {
// Could not dump the heap, abort.
return;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
什么時(shí)候使用RefWatcher進(jìn)行監(jiān)控 辈讶?
對(duì)于android, 若要監(jiān)控Activity娄猫, 需要在其執(zhí)行destroy的 時(shí)候進(jìn)行監(jiān)控:
通過(guò)向Application 注冊(cè) ActivityLifecycleCallback贱除, 在onActivityDestroyed(Activity activity) 中 開(kāi)始監(jiān)聽(tīng) activity對(duì)象生闲, 因?yàn)檫@時(shí)activity應(yīng)該被回收了,若發(fā)生內(nèi)存泄露勘伺,則可以沒(méi)發(fā)現(xiàn)跪腹;
RefWatcher 檢查對(duì)象是否被回收是在一個(gè) Executor 中執(zhí)行的, Android 的監(jiān)控 提供了 AndroidWatchExecutor 飞醉, 它在主線程執(zhí)行冲茸, 但是有一個(gè)delay 時(shí)間(默認(rèn)5000 milisecs), 因?yàn)閷?duì)于application 來(lái)說(shuō)缅帘,執(zhí)行destroy activity只是把必要資源回收轴术,activity 對(duì)象不一定會(huì)馬上被 gc回收。
private void executeDelayedAfterIdleUnsafe(final Runnable runnable) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
backgroundHandler.postDelayed(runnable, DELAY_MILLIS);
return false;
}
});
}
ActivityRefWatcher:
package com.squareup.leakcanary;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
import static com.squareup.leakcanary.Preconditions.checkNotNull;
@TargetApi(ICE_CREAM_SANDWICH) public final class ActivityRefWatcher {
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
if (SDK_INT < ICE_CREAM_SANDWICH) {
// If you need to support Android < ICS, override onDestroy() in your base activity.
return;
}
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
activityRefWatcher.watchActivities();
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
private final Application application;
private final RefWatcher refWatcher;
/**
- Constructs an {@link ActivityRefWatcher} that will make sure the activities are not leaking
- after they have been destroyed.
*/
public ActivityRefWatcher(Application application, final RefWatcher refWatcher) {
this.application = checkNotNull(application, "application");
this.refWatcher = checkNotNull(refWatcher, "refWatcher");
}
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
}
若發(fā)生了泄露钦无, refWatcher 會(huì)執(zhí)行dump 逗栽,生成dump 文件,然后由mat 或haha 等分析工具找到泄露對(duì)象的引用路徑失暂。
作者: DocMike
鏈接:https://www.imooc.com/article/19225
來(lái)源:慕課網(wǎng)