leakcanary1.5源码分析

介绍

内存泄漏检测工具,square公司出品,github地址https://github.com/square/leakcanary。
gradle引用:

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'

使用

在自定义application中onCreat方法中调用下面代码。默认只是检测所有activity中的内存泄漏。如果想检测fragment、service等的内存泄漏,需要在相应的类的onDestory方法中调用refWatcher.watch(this)即可。然后在使用app的过程中,如果出现内存泄漏就会在leaks的app中listview展示出来,并且有相应的路径。

     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); 

问题

使用特别简单,但是想了想,有几个问题值得思考。
如何检测到所有activity的内存泄漏的?
内存泄漏检测机制是什么?
内存泄漏轨迹的生成过程 ?

源码分析

    public static RefWatcher install(Application application) {
    return refWatcher(application)//创建引用监听者
        .listenerServiceClass(DisplayLeakService.class)//显示泄漏信息service
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
    }

以上为install源码,不难发现buildAndInstall是一个build模式,一般build都是一些校验或者初始化。

    public RefWatcher buildAndInstall() {
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      LeakCanary.enableDisplayLeakActivity(context);
      ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);
    }
    return refWatcher;
    }

然后build之后创建一个RefWatcher对象。

    public final RefWatcher build() {
    if (isDisabled()) {
      return RefWatcher.DISABLED;
    }


    ExcludedRefs excludedRefs = this.excludedRefs;
    if (excludedRefs == null) {
      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();
    }

    return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
        excludedRefs);
    }

创建完对象之后,enableDisplayLeakActivity方法是否展示泄漏activity,最重要的是ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher)处理。

    @TargetApi(ICE_CREAM_SANDWICH) public final class ActivityRefWatcher {

    public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
    if (SDK_INT < ICE_CREAM_SANDWICH) {
      // If you need to support Android < ICS, override onDestroy() in your base activity.
      return;
    }
    ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
    activityRefWatcher.watchActivities();
    }

    private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new 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) {
          ActivityRefWatcher.this.onActivityDestroyed(activity);
        }
      };

    private final Application application;
    private final RefWatcher refWatcher;

    /**
     * Constructs an {@link ActivityRefWatcher} that will make sure the activities     are not leaking
       * after they have been destroyed.
     */
    public ActivityRefWatcher(Application application, final RefWatcher refWatcher) {
    this.application = checkNotNull(application, "application");
    this.refWatcher = checkNotNull(refWatcher, "refWatcher");
    }

    void onActivityDestroyed(Activity activity) {
    refWatcher.watch(activity);
    }

    public void watchActivities() {
    // Make sure you don't get installed twice.
    stopWatchingActivities();
    application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
    }

    public void stopWatchingActivities() {
    application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
    }
    }

installOnIcsPlus方法先判断版本<14 不能使用,然后调用创建ActivityRefWatcher对象,调用watchActivities方法,watchActivities里面就是将传入的application注册registerActivityLifecycleCallbacks用来获取这个application的所有activity的生命周期。然后在Application.ActivityLifecycleCallbacks回调中onActivityDestroyed生命周期里面调用ActivityRefWatcher.this.onActivityDestroyed(activity)方法,最后调用到refWatcher.watch(activity),接下来进入watch来看下。

    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);
    }

首先根据uuid创建一个key放入retainedKeys数组里面,添加弱引用的标识,KeyedWeakReference是继承WeakReference的类,一个弱引用的封装。看到
new KeyedWeakReference(watchedReference, key, referenceName, queue),是在创建一个弱引用,并放到ReferenceQueue引用队列中。KeyedWeakReference里面日常参数校验,以及调用WeakReference的super方法。

    final class KeyedWeakReference extends WeakReference {
    public final String key;
    public final String name;

    KeyedWeakReference(Object referent, String key, String name,
      ReferenceQueue referenceQueue) {
    super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
    this.key = checkNotNull(key, "key");
    this.name = checkNotNull(name, "name");
    }
    }
 
 

接下来查看ensureGoneAsync方法。

     private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    watchExecutor.execute(new Retryable() {
      @Override public Retryable.Result run() {
        return ensureGone(reference, watchStartNanoTime);
      }
    });
    }

    Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    //清除此时已经到ReferenceQueue中的弱引用
    removeWeaklyReachableReferences();

    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    //没有内存泄漏
    if (gone(reference)) {
      return DONE;
    }

    //gc处理
    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);
    //生成hprof文件并进行分析hprof文件
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));
    }
    return DONE;
    }

源码大概就是先清除弱引用的队列,然后查看引用是否存在队列中,
.if (gone(reference)) 用来判断某个 reference 的 key 是否仍在 retainedKeys 里,若不在,表示已回收,否则继续。然后在手动gc,之后再清除弱引用,在查看是否弱引用队列中是否存在,如果存在证明有内存泄露,就生成hprof文件并分析hprof文件。
removeWeaklyReachableReferences方法是清除此时已经到ReferenceQueue中的弱引用,gone方法是检测存放key的数组是否存在key。

    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);
    }
    }

File heapDumpFile = heapDumper.dumpHeap(),生成hprof文件的具体实现在AndroidHeapDumper类中。

    public final class AndroidHeapDumper implements HeapDumper {
    private final Context context;
    private final LeakDirectoryProvider leakDirectoryProvider;
    private final Handler mainHandler;

    public AndroidHeapDumper(Context context, LeakDirectoryProvider   leakDirectoryProvider) {
    this.leakDirectoryProvider = leakDirectoryProvider;
    this.context = context.getApplicationContext();
    mainHandler = new Handler(Looper.getMainLooper());
    }

    @Override public File dumpHeap() {
    File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();

    if (heapDumpFile == RETRY_LATER) {
      return RETRY_LATER;
    }

    FutureResult waitingForToast = new FutureResult<>();
    showToast(waitingForToast);

    if (!waitingForToast.wait(5, SECONDS)) {
      CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
      return RETRY_LATER;
    }

    Toast toast = waitingForToast.get();
    try {
      Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
      cancelToast(toast);
      return heapDumpFile;
    } catch (Exception e) {
      CanaryLog.d(e, "Could not dump heap");
      // Abort heap dump
      return RETRY_LATER;
    }
    }

    private void showToast(final FutureResult waitingForToast) {
    mainHandler.post(new Runnable() {
      @Override public void run() {
        final Toast toast = new Toast(context);
        toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
        toast.setDuration(Toast.LENGTH_LONG);
        LayoutInflater inflater = LayoutInflater.from(context);
        toast.setView(inflater.inflate(R.layout.leak_canary_heap_dump_toast, null));
        toast.show();
        // Waiting for Idle to make sure Toast gets rendered.
        Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
          @Override public boolean queueIdle() {
            waitingForToast.set(toast);
            return false;
          }
        });
      }
    });
    }

    private void cancelToast(final Toast toast) {
    mainHandler.post(new Runnable() {
      @Override public void run() {
        toast.cancel();
      }
    });
    }
    }

分析的具体实现在HeapAnalyzerService中,HeapAnalyzerService是一个IntentService。

    public final class HeapAnalyzerService extends IntentService {

    private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
    private static final String HEAPDUMP_EXTRA = "heapdump_extra";

    public static void runAnalysis(Context context, HeapDump heapDump,
      Class listenerServiceClass) {
    Intent intent = new Intent(context, HeapAnalyzerService.class);
    intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
    intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    context.startService(intent);
    }

    public HeapAnalyzerService() {
    super(HeapAnalyzerService.class.getSimpleName());
    }

    @Override protected void onHandleIntent(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);

    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
    }
    }

总结

如何检测到所有activity的内存泄漏的?
通过application注册registerActivityLifecycleCallbacks,来监听说有activity的生命周期,并在onDestory中监听。

内存泄漏检测机制是什么?
若 Activity 被正常回收,就把activity使用KeyedWeakReference创建一个弱引用,引用它的 KeyedWeakReference 会被自动放入 ReferenceQueue 中。首先先移除不可达用引用,如果当前引用不存在,就不继续了,然后gc,再次移除不可达引用,如果当前引用不存在,就不继续了。如果两次移除后都没有被回收,就根据队列的弱引用,生成HeapDump信息。

内存泄漏轨迹的生成过程 ?
利用 heapDumper 把内存情况 dump 成文件,利用 HeapAnalyzer 对 dump 的内存情况进行分析并进一步确认,若确定发生泄漏,调用 DisplayLeakService发送通知。

你可能感兴趣的:(leakcanary1.5源码分析)