LeakCanary源码分析
LeakCanary是一个内存泄漏检测的框架,默认只会检测Activity的泄漏,如果需要检测其他类,可以使用LeakCanary.install返回的RefWatcher,调用RefWatcher.watch(obj)就可以观测obj对象是否出现泄漏。
从install方法开始:
public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
最终返回了一个RefWatcher对象:
public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanary.enableDisplayLeakActivity(context);
ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;
}
DisplayLeakService
继承自AbstractAnalysisResultService,它是一个IntentService,用来对泄漏数据进行分析处理。
public abstract class AbstractAnalysisResultService extends IntentService {
...
@Override protected final void onHandleIntent(Intent intent) {
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAP_DUMP_EXTRA);
AnalysisResult result = (AnalysisResult) intent.getSerializableExtra(RESULT_EXTRA);
try {
onHeapAnalyzed(heapDump, result);
} finally {
//noinspection ResultOfMethodCallIgnored
heapDump.heapDumpFile.delete();
}
}
}
在AbstractAnalysisResultService的实现DisplayLeakService中,会开启一个DisplayLeakActivity活动进行泄漏的显示:
public class DisplayLeakService extends AbstractAnalysisResultService {
@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
String leakInfo = leakInfo(this, heapDump, result, true);
CanaryLog.d("%s", leakInfo);
boolean resultSaved = false;
boolean shouldSaveResult = result.leakFound || result.failure != null;
if (shouldSaveResult) {
heapDump = renameHeapdump(heapDump);
resultSaved = saveResult(heapDump, result); //保存heapdump到本地
}
PendingIntent pendingIntent;
String contentTitle;
String contentText;
if (!shouldSaveResult) {
contentTitle = getString(R.string.leak_canary_no_leak_title);
contentText = getString(R.string.leak_canary_no_leak_text);
pendingIntent = null;
} else if (resultSaved) { //heapdump文件保存成功才开启活动
pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
if (result.failure == null) {
String size = formatShortFileSize(this, result.retainedHeapSize);
String className = classSimpleName(result.className);
if (result.excludedLeak) {
contentTitle = getString(R.string.leak_canary_leak_excluded, className, size);
} else {
contentTitle = getString(R.string.leak_canary_class_has_leaked, className, size);
}
} else {
contentTitle = getString(R.string.leak_canary_analysis_failed);
}
contentText = getString(R.string.leak_canary_notification_message);
} else {
contentTitle = getString(R.string.leak_canary_could_not_save_title);
contentText = getString(R.string.leak_canary_could_not_save_text);
pendingIntent = null;
}
// New notification id every second.
int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
afterDefaultHandling(heapDump, result, leakInfo); //最后调用一个扩展的接口,我们可以重写这个方法来实现heapdump上传到服务器的功能
}
}
RefWatch
RefWatch是最主要的类:
我们看一下watch方法:
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); //retainedKeys存放未被GC回收的被观察对象
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);
}
});
}
检查引用是否有效;GC;再次检查引用是否有效。
弱引用的关联引用队列:当弱引用的对象,弱引用会放入关联的引用队列queue中。
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
long gcStartNanoTime = System.nanoTime(); //gc开始时间
long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime); //已经观察的时间
removeWeaklyReachableReferences(); //把有效引用(线程可达)移出弱引用集合
if (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) { //如果弱引用队列里面已经没有被观测对象的引用了,表示这个对象已经被回收。
return DONE;
}
gcTrigger.runGc(); //执行GC,并且等待100ms
removeWeaklyReachableReferences(); //再次移出有效引用
if (!gone(reference)) { //如果这时候被观测对象的引用不等于null,也就是它仍然被线程可达,说明已经发生了内存泄漏
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime); //计算gc所用时间
File heapDumpFile = heapDumper.dumpHeap(); //把堆栈信息存到本地
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)); //最后交给heapdump监听器来处理
}
return DONE;
}
heapdump信息最终会传递给继承自IntentService的listenerServiceClass方法处理:
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);
}
AndroidWatchExecutor
我们来看一下内存泄漏检查的执行器:
public AndroidWatchExecutor(long initialDelayMillis) {
mainHandler = new Handler(Looper.getMainLooper()); //主线程Handler
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME); //新建一个带消息循环Looper的子线程
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper()); //后台Handler
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
执行检查任务:
@Override public void execute(Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0); //主线程
} else {
postWaitForIdle(retryable, 0); //非主线程
}
最终是在主线程调用waitForIdle:
void waitForIdle(final Retryable retryable, final int failedAttempts) {
// This needs to be called from the main thread.
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { //添加一个闲置处理器(在主线程消息队列闲置的时候会执行queueIdle)
@Override public boolean queueIdle() {
postToBackgroundWithDelay(retryable, failedAttempts);
return false;
}
});
}
void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
long delayMillis = initialDelayMillis * exponentialBackoffFactor;
backgroundHandler.postDelayed(new Runnable() { //把任务放到后台线程的消息队列中
@Override public void run() {
Retryable.Result result = retryable.run(); //这时候才会执行针对对象的内存泄漏检查
if (result == RETRY) {
postWaitForIdle(retryable, failedAttempts + 1);
}
}
}, delayMillis);
}
ActivityRefWatcher
这个类是用来监听一个应用内的所有活动的生命周期,当活动执行了onDestory的时候,开始对Activity内存泄漏的检查。
void onActivityDestroyed(Activity activity) {
refWatcher.watch(activity);
}
总结
整个Activity内存泄漏的检测从Activity执行了onDestory后开始:
1. Activity执行onDestory
2. refWatcher把该Activity添加到弱引用列表里面
3. 然后使用IdelHandler机制,在主线程空闲的时候延时 5 秒发送一个Runnable到后台线程HandlerThread的消息队列里
4. HandlerThread取出消息,移除被回收对象的弱引用,然后查找活动是否还存在于弱引用列表中,如果不在,则说明没有发生内存泄漏
5. 如果在,则调用一次gc,等待100ms,再次移除被回收对象的弱引用,查找活动是否还存在弱引用列表里面,如果在,则说明发生了内存泄漏
6. 然后就执行dumpheap,把堆栈信息用Intent的方式发送给IntentService
7. IntentService拿到dumheap之后,保存heapdump到本地,在通知栏上显示内存泄漏的通知,然后执行afterDefaultHandling(我们可以重写)
8. 最后删除本地的heapdump文件
几个问题
1. 为什么要使用idlehandler? 在主线程空闲的时候才把任务放到子线程中执行
2. HandlerThread会退出吗?
改进,用HandlerThread设置为守护线程,当主线程退出时,该线程也就死亡了:
handlerThread.setDaemon(true);
3. 线程安全吗?安全retainedKeys = new CopyOnWriteArraySet<>();