LeakCanary芭商,在開(kāi)發(fā)階段,可以用來(lái)檢測(cè)內(nèi)存泄露,項(xiàng)目地址:https://github.com/square/leakcanary
具體操作:1再愈、在Application 的onCreate()初始化,會(huì)ActivityLifecycleCallbacks監(jiān)聽(tīng)所有的activity的onDestroy护戳,
2翎冲、把a(bǔ)ctivity生成對(duì)應(yīng)key的放到集合Set<String> retainedKeys,并生成帶Key的弱引用媳荒,GC在回收的時(shí)候抗悍,如果是弱引用,會(huì)把這個(gè)回收對(duì)象放到ReferenceQueue<T> queue钳枕,如果poll出來(lái)有對(duì)象缴渊,說(shuō)明這個(gè)activity對(duì)象已回收,移除集合包含鱼炒,若沒(méi)有衔沼,判斷集合是否包含key,若有,則就懷疑是泄漏了指蚁,需要二次確認(rèn)菩佑,進(jìn)行手動(dòng)Runtime.getRuntime().gc(),
3凝化、泄露對(duì)象稍坯,生成HPROF文件,分析這個(gè)快照文件有沒(méi)有存在帶這個(gè)key值的泄漏對(duì)象搓劫,如果沒(méi)有劣光,那么沒(méi)有泄漏,
否則找出最短路徑糟把,打印給我們绢涡,就找到這個(gè)泄漏對(duì)象了。
一遣疯、初始化LeakCanary
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}
public final class LeakCanary {
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
}
refWatcher()生成AndroidRefWatcherBuilder類(lèi)雄可,.buildAnInstall(),會(huì)生成RefWatcher缠犀,并把相應(yīng)的對(duì)象如(GcTrigger数苫、AndroidWatchExecutor、AndroidHeapDumper辨液、ServiceHeapDumpListener虐急、AndroidExcludedRefs等)。
且Application.registerActivityLifecycleCallbacks這個(gè)方法滔迈,用來(lái)統(tǒng)一管理所有activity的生命周期止吁,LeakCanary就是在onDestory()方法實(shí)現(xiàn)監(jiān)控的.
public final class ActivityRefWatcher {
...
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new Application.ActivityLifecycleCallbacks() {
...
@Override public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};
private final Application application;
private final RefWatcher refWatcher;
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
public void watchActivities() {
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
}
二、泄露判斷
在RefWatcher中watch函數(shù)中:
public final class RefWatcher {
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
private final HeapDump.Listener heapdumpListener;
private final ExcludedRefs excludedRefs;
public void watch(Object watchedReference, String referenceName) {
...
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
其中retainedKeys是用來(lái)保存key的集合燎悍,KeyedWeakReference是帶有key的弱引用對(duì)象敬惦,queue是ReferenceQueue,用來(lái)GC時(shí)候谈山,如果弱引用的對(duì)象被回收了俄删, 系統(tǒng)會(huì)把這個(gè)回收對(duì)象放到queue里面。這樣就可以判斷acitivity是否泄漏了奏路。
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
public final class AndroidWatchExecutor implements WatchExecutor {
...
@Override public void execute(Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
void waitForIdle(final Retryable retryable, final int failedAttempts) {
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
}
watchExecutor是AndroidWatchExecutor畴椰,在waitForIdle中有一個(gè)Looper.myQueue().addIdleHandler,這個(gè)是在當(dāng)前線程中取出MessageQueue對(duì)象鸽粉,添加一個(gè)idleHandler斜脂,在Looper,loop()循環(huán)取出Message時(shí),如果沒(méi)有message對(duì)象來(lái)潜叛,即Looper處在空閑時(shí)秽褒,會(huì)實(shí)行這個(gè)idleHandler壶硅,相當(dāng)于在UI線程中頁(yè)面的操作如onDestroy威兜、onStop等跟AMS通訊完成后销斟,沒(méi)有handler.post消息message過(guò)來(lái)時(shí),就實(shí)行了idleHandler椒舵。
public final class Looper {
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
...
}
}
}
public final class MessageQueue {
Message next() {
...
for (;;) {
...
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
...(return msg)
}
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
}
}
接著實(shí)行核心方法ensureGone:
@SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime();
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
removeWeaklyReachableReferences();//看下檢測(cè)的弱引用回收了沒(méi)
...
if (gone(reference)) {//好蚂踊,回收了,那么這個(gè)activity沒(méi)有泄漏
return DONE;
}
gcTrigger.runGc();//還是沒(méi)有回收笔宿,手動(dòng)GC一下
removeWeaklyReachableReferences();//再看看對(duì)象回收沒(méi)有
if (!gone(reference)) {//還沒(méi)回收犁钟,懷疑是內(nèi)存泄漏了,dump內(nèi)存快照.hprof下來(lái)分析
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();//生產(chǎn)快照f(shuō)ile
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
private boolean gone(KeyedWeakReference reference) {
return !retainedKeys.contains(reference.key);
}
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
retainedKeys.remove(ref.key);
}
}
}
三泼橘、hrop內(nèi)存快照生成
解析hprof文件中涝动,是先把這個(gè)文件封裝成snapshot,然后根據(jù)弱引用和前面定義的key值,確定泄漏的對(duì)象炬灭,最后找到最短泄漏路徑醋粟,作為結(jié)果反饋出來(lái):
File heapDumpFile = heapDumper.dumpHeap();
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
生產(chǎn)file,進(jìn)入ServiceHeapDumpListener的analyze函數(shù):
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
緊接著調(diào)用HeapAnalyzerService.runAnalysis:
public final class HeapAnalyzerService extends IntentService {
public static void runAnalysis(Context context, HeapDump heapDump,
Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
Intent intent = new Intent(context, HeapAnalyzerService.class);
intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
intent.putExtra(HEAPDUMP_EXTRA, heapDump);
context.startService(intent);
}
}
startService:DisplayLeakService 中:
public class DisplayLeakService extends AbstractAnalysisResultService {
@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
String leakInfo = leakInfo(this, heapDump, result, true);
...
if (shouldSaveResult) {
heapDump = renameHeapdump(heapDump);
resultSaved = saveResult(heapDump, result);
}
PendingIntent pendingIntent;
...
pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
afterDefaultHandling(heapDump, result, leakInfo);
}