LeakCanary,在开发阶段,可以用来检测内存泄露,项目地址:https://github.com/square/leakcanary
具体操作:1、在Application 的onCreate()初始化,会ActivityLifecycleCallbacks监听所有的activity的onDestroy,
2、把activity生成对应key的放到集合Set<String> retainedKeys,并生成带Key的弱引用,GC在回收的时候,如果是弱引用,会把这个回收对象放到ReferenceQueue<T> queue,如果poll出来有对象,说明这个activity对象已回收,移除集合包含,若没有,判断集合是否包含key,若有,则就怀疑是泄漏了,需要二次确认,进行手动Runtime.getRuntime().gc(),
3、泄露对象,生成HPROF文件,分析这个快照文件有没有存在带这个key值的泄漏对象,如果没有,那么没有泄漏,
否则找出最短路径,打印给我们,就找到这个泄漏对象了。
一、初始化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类,.buildAnInstall(),会生成RefWatcher,并把相应的对象如(GcTrigger、AndroidWatchExecutor、AndroidHeapDumper、ServiceHeapDumpListener、AndroidExcludedRefs等)。
且Application.registerActivityLifecycleCallbacks这个方法,用来统一管理所有activity的生命周期,LeakCanary就是在onDestory()方法实现监控的.
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函数中:
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是用来保存key的集合,KeyedWeakReference是带有key的弱引用对象,queue是ReferenceQueue,用来GC时候,如果弱引用的对象被回收了, 系统会把这个回收对象放到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中有一个Looper.myQueue().addIdleHandler,这个是在当前线程中取出MessageQueue对象,添加一个idleHandler,在Looper,loop()循环取出Message时,如果没有message对象来,即Looper处在空闲时,会实行这个idleHandler,相当于在UI线程中页面的操作如onDestroy、onStop等跟AMS通讯完成后,没有handler.post消息message过来时,就实行了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);
}
}
}
}
}
}
接着实行核心方法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();//看下检测的弱引用回收了没
...
if (gone(reference)) {//好,回收了,那么这个activity没有泄漏
return DONE;
}
gcTrigger.runGc();//还是没有回收,手动GC一下
removeWeaklyReachableReferences();//再看看对象回收没有
if (!gone(reference)) {//还没回收,怀疑是内存泄漏了,dump内存快照.hprof下来分析
long startDumpHeap = System.nanoTime();
long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
File heapDumpFile = heapDumper.dumpHeap();//生产快照file
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内存快照生成
解析hprof文件中,是先把这个文件封装成snapshot,然后根据弱引用和前面定义的key值,确定泄漏的对象,最后找到最短泄漏路径,作为结果反馈出来:
File heapDumpFile = heapDumper.dumpHeap();
heapdumpListener.analyze(
new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
gcDurationMs, heapDumpDurationMs));
生产file,进入ServiceHeapDumpListener的analyze函数:
@Override public void analyze(HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
紧接着调用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);
}