参考链接
Java内存问题 及 LeakCanary 原理分析
LeakCanary 工作原理浅析
通过 application.registerActivityLifecycleCallbacks 监听activity onActivityDestroy()
用过WeakReference+ReferenceQueue 存储当前activity 的引用,手动调用gc
然后判断ReferenceQueue队列是否含有此activity的引用,若不包含,说明被强引用,存在内存泄漏,然后导出heap dump 文件,用haha算法分析,在发送到通知栏里
public final class ActivityRefWatcher {
public static void install(Context context, RefWatcher refWatcher) {
Application application = (Application) context.getApplicationContext();
ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}
private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
new ActivityLifecycleCallbacksAdapter() {
@Override public void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
};
}
public final class RefWatcher {
public void watch(Object watchedReference, String referenceName) {
if (this == DISABLED) {
return;
}
checkNotNull(watchedReference, "watchedReference");
checkNotNull(referenceName, "referenceName");
final long watchStartNanoTime = System.nanoTime();
String key = UUID.randomUUID().toString();
retainedKeys.add(key);
//创建WeakReference
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
}
public final class RefWatcher {
@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();//先将引用尝试从队列中poll出来
if (debuggerControl.isDebuggerAttached()) {//规避调试模式
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {//检测是否已经回收
return DONE;
}
//如果没有被回收,则手动GC
gcTrigger.runGc();
removeWeaklyReachableReferences();//再次尝试poll,检测是否被回收
if (!gone(reference)) {
// 还没有被回收,则dump堆信息,调起分析进程进行分析
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();
if (heapDumpFile == RETRY_LATER) {
// Could not dump the heap.
return RETRY;
}
long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
.referenceName(reference.name)
.watchDurationMs(watchDurationMs)
.gcDurationMs(gcDurationMs)
.heapDumpDurationMs(heapDumpDurationMs)
.build();
heapdumpListener.analyze(heapDump);
}
return DONE;
}
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);
}
}
}
方法ensureGone中通过检测referenceQueue队列中的引用情况,来判断回收情况,通过手动GC来进一步确认回收情况。
整个过程肯定是个耗时卡UI的,整个过程会在WatchExecutor中执行的,那WatchExecutor又是在哪里执行的呢?
LeakCanary已经利用Looper机制做了一定优化,利用主线程空闲的时候执行检测任务,这里找到WatchExecutor的实现类,研究下原理:
public final class AndroidWatchExecutor implements WatchExecutor {
private void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
}
知识点
- 用ActivityLifecycleCallback 监听activity生命周期
- 用WeekReference+ReferenceQueue 来监听activity回收情况
- Application 中可通过processName 判断是否任务执行进程
- MessageQueue 中加入一个IdleHandler 来得到主线程空闲的回调
- 只能监听activity里相关类内存泄漏,其他类无法使用,还得用MAT原始方法
网友评论