LeakCanary 是一个非常强大的内存泄露监测工具,可以实现打印内存泄露的信息。Android 的内存泄露检测主要是对Activity的回收情况检测,如果Activity调用了onDestroy方法,没有及时回收就是意味着出现了内存泄露,Android Studio 提供的内存泄露工具就是通过这种方式实现的,然后通过dump分析对应的依赖情况,LeakCanary的原理也是如此,下面我们通过源码分析来验证这一点。
LeakCanary 初始化
LeakCanary.install(this);
LeakCanary 2.0 的结构已经改变了,不再是通过这种方式初始化。
public final class LeakCanary {
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
}
通过阅读LeakCanary的源码,结合AndroidRefWatcherBuilder类,可以看到最终创建ActivityRefWatcher、AndroidOFragmentRefWatcher。
ActivityRefWatcher
ActivityRefWatcher的代码量不多,关键的信息就是 application.registerActivityLifecycleCallbacks,从这里验证了我们的初步想法,就是通过监听 Activity的生命周期,在onActivityDestroyed的时候把Activity丢进监听的任务中。
public final class ActivityRefWatcher {
public static void install(@NonNull Context context, @NonNull 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);
}
};
...
}
AndroidOFragmentRefWatcher
上面的ActivityRefWatcher是对Activity的监听,AndroidOFragmentRefWatcher是对Fragment进行监听,不过仅仅支持Android O以上的版本。
@RequiresApi(Build.VERSION_CODES.O) //
class AndroidOFragmentRefWatcher implements FragmentRefWatcher {
private final RefWatcher refWatcher;
AndroidOFragmentRefWatcher(RefWatcher refWatcher) {
this.refWatcher = refWatcher;
}
private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
new FragmentManager.FragmentLifecycleCallbacks() {
@Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
View view = fragment.getView();
if (view != null) {
refWatcher.watch(view);
}
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
refWatcher.watch(fragment);
}
};
@Override public void watchFragments(Activity activity) {
FragmentManager fragmentManager = activity.getFragmentManager();
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}
}
RefWatcher
接下来我们看看 refWatcher.watch这个方法,这里是检测调用了onDestroy的Activity,然后调用了ensureGoneAsync
的方法,这里可以看到,使用了WatchExecutor执行异步任务,他的实现类是AndroidWatchExecutor
,是基于HandlerThread实现,这里就不展开。核心是ensureGone
方法,这个方法中,调用了 gcTrigger.runGc()
对当前的内存进行回收,然后调用removeWeaklyReachableReferences
移除已经回收的对象,经过内存回收如果对象还没有被回收就说明这个对象(Activity、Fragment)存在了内存泄露问题了,接下来就是通过 HeapDump
类获取内存的镜像信息保存到文件中,然后通过heapdumpListener.analyze
进一步分析。
public final class RefWatcher {
public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
private final WatchExecutor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final HeapDump.Listener heapdumpListener;
private final HeapDump.Builder heapDumpBuilder;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
public void watch(Object watchedReference) {
watch(watchedReference, "");
}
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);
final KeyedWeakReference reference =
new KeyedWeakReference(watchedReference, key, referenceName, queue);
ensureGoneAsync(watchStartNanoTime, reference);
}
/**
* LeakCanary will stop watching any references that were passed to {@link #watch(Object, String)}
* so far.
*/
public void clearWatchedReferences() {
retainedKeys.clear();
}
boolean isEmpty() {
removeWeaklyReachableReferences();
return retainedKeys.isEmpty();
}
HeapDump.Builder getHeapDumpBuilder() {
return heapDumpBuilder;
}
Set<String> getRetainedKeys() {
return new HashSet<>(retainedKeys);
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
@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 (debuggerControl.isDebuggerAttached()) {
// The debugger can create false leaks.
return RETRY;
}
if (gone(reference)) {
return DONE;
}
gcTrigger.runGc();
removeWeaklyReachableReferences();
if (!gone(reference)) {
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);
}
}
}
获取dump的方式:Debug.dumpHprofData(heapDumpFile.absolutePath)
ServiceHeapDumpListener
在上一步已经生成了Dump文件,在这里监听器中,启动了一个HeapAnalyzerService前台服务,通过前台服务进行下一步分析。
public final class ServiceHeapDumpListener implements HeapDump.Listener {
private final Context context;
private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;
public ServiceHeapDumpListener(@NonNull final Context context,
@NonNull final Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
this.context = checkNotNull(context, "context").getApplicationContext();
}
@Override public void analyze(@NonNull HeapDump heapDump) {
checkNotNull(heapDump, "heapDump");
HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}
}
HeapAnalyzerService
从这个服务中使用HeapAnalyzer类对heapDump文件进行分析。
public final class HeapAnalyzerService extends ForegroundService
implements AnalyzerProgressListener {
@Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
if (intent == null) {
CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
return;
}
String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
HeapAnalyzer heapAnalyzer =
new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
heapDump.computeRetainedHeapSize);
AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}
}
最后
到这里就不再往下面分析,对Dump的分析是一个非常复杂的过程,这里用了一个叫haha
的库,也是square开发的,好调皮的名字。
com.squareup.haha:haha:2.0.4
网友评论