LeakCanary是一个内存泄漏检测的框架,简单的搭建就可以帮助程序员发现自己app中内存泄漏情况,并通过界面展示,文件生成的方式,告知程序员泄漏链。
题外话:前几篇文章有同学反映老是采用近乎于肢解的方式解析代码,可能一遍下来就作者自己明白了,其他人还是云里雾里。主要现在我没找到好的绘图工具,画各种流程图,类图,交互图,等我这方面能力提升后,补充。现在仍以源码的逐行分析。。。看的痛苦也需要耐心。
下面来看一个简单的Application中的设置
LeakCanary自己开了一个分析进程,不干扰用户自己程序。这里要判断该进程不是分析进程。LeakCanary.install(this)完成配置。
private void initLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
}
install方法返回RefWatcher对象,builder创建。listenerServiceClass设置结果反馈的DisplayLeakService。excludedRefs先排除一些android平台默认的问题或者不用考虑的检测项,专注于用户自己的app。
public static @NonNull RefWatcher install(@NonNull Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class).excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
.buildAndInstall();
}
public static @NonNull AndroidRefWatcherBuilderrefWatcher(@NonNull Context context) {
return new AndroidRefWatcherBuilder(context);
}
RefWatcherBuilder是个范型builder,注意他的范型参数很有意思<T extends RefWatcherBuilder>,这个T用于Builder的返回,链式调用。
public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
private HeapDump.Listener heapDumpListener;
private DebuggerControl debuggerControl;
private HeapDumper heapDumper;
private WatchExecutor watchExecutor;
private GcTrigger gcTrigger;
private final HeapDump.Builder heapDumpBuilder;
public RefWatcherBuilder() {
heapDumpBuilder = new HeapDump.Builder();
}
/** @see HeapDump.Listener */
public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
this.heapDumpListener = heapDumpListener;
return self();
}
/** @see ExcludedRefs */
public final T excludedRefs(ExcludedRefs excludedRefs) {
heapDumpBuilder.excludedRefs(excludedRefs);
return self();
}
/** @see HeapDumper */
public final T heapDumper(HeapDumper heapDumper) {
this.heapDumper = heapDumper;
return self();
}
/** @see DebuggerControl */
public final T debuggerControl(DebuggerControl debuggerControl) {
this.debuggerControl = debuggerControl;
return self();
}
/** @see WatchExecutor */
public final T watchExecutor(WatchExecutor watchExecutor) {
this.watchExecutor = watchExecutor;
return self();
}
/** @see GcTrigger */
public final T gcTrigger(GcTrigger gcTrigger) {
this.gcTrigger = gcTrigger;
return self();
}
/** @see Reachability.Inspector */
public final T stethoscopeClasses(
List<Class<? extends Reachability.Inspector>> stethoscopeClasses) {
heapDumpBuilder.reachabilityInspectorClasses(stethoscopeClasses);
return self();
}
/**
* Whether LeakCanary should compute the retained heap size when a leak is detected. False by
* default, because computing the retained heap size takes a long time.
*/
public final T computeRetainedHeapSize(boolean computeRetainedHeapSize) {
heapDumpBuilder.computeRetainedHeapSize(computeRetainedHeapSize);
return self();
}
/** Creates a {@link RefWatcher}. */
public final RefWatcher build() {
if (isDisabled()) {
return RefWatcher.DISABLED;
}
if (heapDumpBuilder.excludedRefs == null) {
heapDumpBuilder.excludedRefs(defaultExcludedRefs());
}
HeapDump.Listener heapDumpListener = this.heapDumpListener;
if (heapDumpListener == null) {
heapDumpListener = defaultHeapDumpListener();
}
DebuggerControl debuggerControl = this.debuggerControl;
if (debuggerControl == null) {
debuggerControl = defaultDebuggerControl();
}
HeapDumper heapDumper = this.heapDumper;
if (heapDumper == null) {
heapDumper = defaultHeapDumper();
}
WatchExecutor watchExecutor = this.watchExecutor;
if (watchExecutor == null) {
watchExecutor = defaultWatchExecutor();
}
GcTrigger gcTrigger = this.gcTrigger;
if (gcTrigger == null) {
gcTrigger = defaultGcTrigger();
}
if (heapDumpBuilder.reachabilityInspectorClasses == null) {
heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
}
return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
heapDumpBuilder);
}
protected boolean isDisabled() {
return false;
}
protected GcTrigger defaultGcTrigger() {
return GcTrigger.DEFAULT;
}
protected DebuggerControl defaultDebuggerControl() {
return DebuggerControl.NONE;
}
protected ExcludedRefs defaultExcludedRefs() {
return ExcludedRefs.builder().build();
}
protected HeapDumper defaultHeapDumper() {
return HeapDumper.NONE;
}
protected HeapDump.Listener defaultHeapDumpListener() {
return HeapDump.Listener.NONE;
}
protected WatchExecutor defaultWatchExecutor() {
return WatchExecutor.NONE;
}
protected List<Class<? extends Reachability.Inspector>> defaultReachabilityInspectorClasses() {
return Collections.emptyList();
}
@SuppressWarnings("unchecked")
protected final T self() {
return (T) this;
}
}
针对Android平台的RefWatcherBuilder。
public final class AndroidRefWatcherBuilder extends RefWatcherBuilder<AndroidRefWatcherBuilder> {
private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);
private final Context context;
private boolean watchActivities = true;
private boolean watchFragments = true;
AndroidRefWatcherBuilder(@NonNull Context context) {
this.context = context.getApplicationContext();
}
/**
* Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
* overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
*/
public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
@NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}
/**
* Sets a custom delay for how long the {@link RefWatcher} should wait until it checks if a
* tracked object has been garbage collected. This overrides any call to {@link
* #watchExecutor(WatchExecutor)}.
*/
public @NonNull AndroidRefWatcherBuilder watchDelay(long delay, @NonNull TimeUnit unit) {
return watchExecutor(new AndroidWatchExecutor(unit.toMillis(delay)));
}
/**
* Whether we should automatically watch activities when calling {@link #buildAndInstall()}.
* Default is true.
*/
public @NonNull AndroidRefWatcherBuilder watchActivities(boolean watchActivities) {
this.watchActivities = watchActivities;
return this;
}
/**
* Whether we should automatically watch fragments when calling {@link #buildAndInstall()}.
* Default is true. When true, LeakCanary watches native fragments on Android O+ and support
* fragments if the leakcanary-support-fragment dependency is in the classpath.
*/
public @NonNull AndroidRefWatcherBuilder watchFragments(boolean watchFragments) {
this.watchFragments = watchFragments;
return this;
}
/**
* Sets the maximum number of heap dumps stored. This overrides any call to
* {@link LeakCanary#setLeakDirectoryProvider(LeakDirectoryProvider)}
*
* @throws IllegalArgumentException if maxStoredHeapDumps < 1.
*/
public @NonNull AndroidRefWatcherBuilder maxStoredHeapDumps(int maxStoredHeapDumps) {
LeakDirectoryProvider leakDirectoryProvider =
new DefaultLeakDirectoryProvider(context, maxStoredHeapDumps);
LeakCanary.setLeakDirectoryProvider(leakDirectoryProvider);
return self();
}
/**
* Creates a {@link RefWatcher} instance and makes it available through {@link
* LeakCanary#installedRefWatcher()}.
*
* Also starts watching activity references if {@link #watchActivities(boolean)} was set to true.
*
* @throws UnsupportedOperationException if called more than once per Android process.
*/
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
@Override protected boolean isDisabled() {
return LeakCanary.isInAnalyzerProcess(context);
}
@Override protected @NonNull HeapDumper defaultHeapDumper() {
LeakDirectoryProvider leakDirectoryProvider =
LeakCanaryInternals.getLeakDirectoryProvider(context);
return new AndroidHeapDumper(context, leakDirectoryProvider);
}
@Override protected @NonNull DebuggerControl defaultDebuggerControl() {
return new AndroidDebuggerControl();
}
@Override protected @NonNull HeapDump.Listener defaultHeapDumpListener() {
return new ServiceHeapDumpListener(context, DisplayLeakService.class);
}
@Override protected @NonNull ExcludedRefs defaultExcludedRefs() {
return AndroidExcludedRefs.createAppDefaults().build();
}
@Override protected @NonNull WatchExecutor defaultWatchExecutor() {
return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
}
@Override protected @NonNull
List<Class<? extends Reachability.Inspector>> defaultReachabilityInspectorClasses() {
return AndroidReachabilityInspectors.defaultAndroidInspectors();
}
}
DEFAULT_WATCH_DELAY_MILLIS算是一个单位,后面可以看到指数延迟方式的尝试会用到它,watchActivities,watchFragments等监控变量是平台特有。
private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);
private final Context context;
private boolean watchActivities = true;
private boolean watchFragments = true;
buildAndInstall方法首先做重复创建检测,build出的RefWatcher不是默认的DISABLED。LeakCanaryInternals是他内部保存静态变量的类。根据设置分别打开ActivityRefWatcher,FragmentRefWatcher.Helper。DisplayLeakActivity是我们看到那个黑色的展示泄露链的界面。
public @NonNull RefWatcher buildAndInstall() {
if (LeakCanaryInternals.installedRefWatcher != null) {
throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
}
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
if (watchActivities) {
ActivityRefWatcher.install(context, refWatcher);
}
if (watchFragments) {
FragmentRefWatcher.Helper.install(context, refWatcher);
}
}
LeakCanaryInternals.installedRefWatcher = refWatcher;
return refWatcher;
}
ActivityRefWatcher类是对Activity监控绑定的类。install方法用application与refWatcher创建一个ActivityRefWatcher。application.registerActivityLifecycleCallbacks把成员变量lifecycleCallbacks 注册到Activity-LifeCycle里面。通过这一波注册。当Activity走到onDestroy方法,会调用refWatcher.watch(activity)进行监控。
public final class ActivityRefWatcher {
public static void installOnIcsPlus(@NonNull Application application,
@NonNull RefWatcher refWatcher) {
install(application, refWatcher);
}
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);
}
};
private final Application application;
private final RefWatcher refWatcher;
private ActivityRefWatcher(Application application, RefWatcher refWatcher) {
this.application = application;
this.refWatcher = refWatcher;
}
public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
public void stopWatchingActivities() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}
}
抽象类ActivityLifecycleCallbacksAdapter是接口ActivityLifecycleCallbacks适配简化版本,就是说只处理onActivityDestroyed的回调就可以了。
public interface ActivityLifecycleCallbacks {
void onActivityCreated(Activity var1, Bundle var2);
void onActivityStarted(Activity var1);
void onActivityResumed(Activity var1);
void onActivityPaused(Activity var1);
void onActivityStopped(Activity var1);
void onActivitySaveInstanceState(Activity var1, Bundle var2);
void onActivityDestroyed(Activity var1);
}
public abstract class ActivityLifecycleCallbacksAdapter
implements Application.ActivityLifecycleCallbacks {
@Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override public void onActivityStarted(Activity activity) {
}
@Override public void onActivityResumed(Activity activity) {
}
@Override public void onActivityPaused(Activity activity) {
}
@Override public void onActivityStopped(Activity activity) {
}
@Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override public void onActivityDestroyed(Activity activity) {
}
}
RefWatcher的watch方法传入activity.首先保证非DISABLED。接着非空检测,记录当前时间,生成UUID唯一码。retainedKeys集合保存当前观测的key集合。
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);
}
KeyedWeakReference继承于WeakReference,key与Activity作为入参。绑定ReferenceQueue,以便被回收时,得到结果
final class KeyedWeakReference extends WeakReference<Object> {
public final String key;
public final String name;
KeyedWeakReference(Object referent, String key, String name,
ReferenceQueue<Object> referenceQueue) {
super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
this.key = checkNotNull(key, "key");
this.name = checkNotNull(name, "name");
}
}
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
watchExecutor.execute(new Retryable() {
@Override public Retryable.Result run() {
return ensureGone(reference, watchStartNanoTime);
}
});
}
Retryable会被以指数级别的延迟重新调用。直到返回Retryable.Result.DONE
public final class AndroidWatchExecutor implements WatchExecutor {
static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";
private final Handler mainHandler;
private final Handler backgroundHandler;
private final long initialDelayMillis;
private final long maxBackoffFactor;
public AndroidWatchExecutor(long initialDelayMillis) {
mainHandler = new Handler(Looper.getMainLooper());
HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
handlerThread.start();
backgroundHandler = new Handler(handlerThread.getLooper());
this.initialDelayMillis = initialDelayMillis;
maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
}
@Override public void execute(@NonNull Retryable retryable) {
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
waitForIdle(retryable, 0);
} else {
postWaitForIdle(retryable, 0);
}
}
private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
mainHandler.post(new Runnable() {
@Override public void run() {
waitForIdle(retryable, failedAttempts);
}
});
}
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;
}
});
}
private 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);
}
}
gcStartNanoTime记录当前时间,watchDurationMs记录已经观测的时间,removeWeaklyReachableReferences更正下观测数据,有可能在这过程中gc回收了观测对象,gone看是否已经不在retainedKeys中了。gcTrigger.runGc()手动GC,继续removeWeaklyReachableReferences更正,发现还没收回掉,那就要开始分析了,分析会生成文件,也会通知栏中展示,点击通知栏打开一个展示的DisplayLeakActivity,DisplayLeakActivity里面列表展示AnalysisResult
@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;
}
核心的东西就是利用组件生命周期回调destroy时候用软引用包装被观测对象,然后指数延迟重试,借助软引用关联的引用队列的结果反馈维护观测对象key集合。如发现未回收,主动调用GC,如果仍未回收便开始执行分析,并通知用户。