LeakCanary源码分析

LeakCanary是一个监测内测泄露的工具,源码地址 https://github.com/square/leakcanary
Android使用方法:
在build.gradle文件中添加

 dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2'
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2'
 }

在Application类里引入LeakCanary.install函数

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}

当出现内存泄漏时,通知栏会弹出通知,点击之后出现详细的内存泄漏信息activity
这里写图片描述

具体源码分析:
1.先进入LeakCanary.install函数

 /**
   * Creates a {@link RefWatcher} that works out of the box, and           starts watching activity
   * references (on ICS+).
   */
  public static RefWatcher install(Application application) {
    return install(application, DisplayLeakService.class,
        AndroidExcludedRefs.createAppDefaults().build());
  }

  /**
   * Creates a {@link RefWatcher} that reports results to the provided service, and starts watching
   * activity references (on ICS+).
   */
  public static RefWatcher install(Application application,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass,
      ExcludedRefs excludedRefs) {
    if (isInAnalyzerProcess(application)) {
      return RefWatcher.DISABLED;
    }
    enableDisplayLeakActivity(application);
    HeapDump.Listener heapDumpListener =
        new ServiceHeapDumpListener(application, listenerServiceClass);
    RefWatcher refWatcher = androidWatcher(application, heapDumpListener, excludedRefs);
    ActivityRefWatcher.installOnIcsPlus(application, refWatcher);
    return refWatcher;
  }

DisplayLeakService.java类负责显示内存泄漏发生的状况,AndroidExcludedRefs.java中可以查看到 一个已知问题的忽略列表,就是说如果发现AndroidExcludedRefs.java类中维护的列表类的内存泄漏,那么在DisplayLeakService并不会显示出来,同时HeapAnalyzer在计算到GC roots的最短强引用路径,也会忽略这些类。
2.isInAnalyzerProcess函数

 /**
   * Whether the current process is the process running the {@link HeapAnalyzerService}, which is
   * a different process than the normal app process.
   */
  public static boolean isInAnalyzerProcess(Context context) {
    return isInServiceProcess(context, HeapAnalyzerService.class);
  }

调用LeakCanaryInternals类的isInServiceProcess函数

public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) {
    PackageManager packageManager = context.getPackageManager();
    PackageInfo packageInfo;
    try {
      packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);
    } catch (Exception e) {
      CanaryLog.d(e, "Could not get package info for %s", context.getPackageName());
      return false;
    }
    String mainProcess = packageInfo.applicationInfo.processName;

    ComponentName component = new ComponentName(context, serviceClass);
    ServiceInfo serviceInfo;
    try {
      serviceInfo = packageManager.getServiceInfo(component, 0);
    } catch (PackageManager.NameNotFoundException ignored) {
      // Service is disabled.
      return false;
    }

    if (serviceInfo.processName.equals(mainProcess)) {
      CanaryLog.d("Did not expect service %s to run in main process %s", serviceClass, mainProcess);
      // Technically we are in the service process, but we're not in the service dedicated process.
      return false;
    }

    int myPid = android.os.Process.myPid();
    ActivityManager activityManager =
        (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.RunningAppProcessInfo myProcess = null;
    List<ActivityManager.RunningAppProcessInfo> runningProcesses =
        activityManager.getRunningAppProcesses();
    if (runningProcesses != null) {
      for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
        if (process.pid == myPid) {
          myProcess = process;
          break;
        }
      }
    }
    if (myProcess == null) {
      CanaryLog.d("Could not find running process for %d", myPid);
      return false;
    }

    return myProcess.processName.equals(serviceInfo.processName);
  }

本函数用于检测传入的参数serviceClass所在的进程是否是服务进程,而不是主进程。首先判断packageInfo.applicationInfo.processName是否和serviceClass所在的进程名相等。然后得到当前进程的pid,遍历目前正在运行的进程,判断pid是否相等,得到当前进程的实例对象,判断是否与serviceClass的所在进程名相等,不在则返回false。
isInAnalyzerProcess函数主要用于判断HeapAnalyzerService函数是否运行在服务进程中,不是则不再执行leakCanary.install函数。

3.enableDisplayLeakActivity(application)函数

public static void enableDisplayLeakActivity(Context context) {
    setEnabled(context, DisplayLeakActivity.class, true);
  }

使能DisplayLeakActivity.class,在Lancher显示LeakCanary图标

4.接下了创建一个heapDumpListener和refWatcher,具体有什么作用,下面的内容会介绍

5.进入 ActivityRefWatcher.installOnIcsPlus函数

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

new出来一个activityRefWatcher并传入application, refWatcher参数

6.进入activityRefWatcher.watchActivities()

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

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

这里注册activity生命周期监听函数
7.让我们来看看它在生命周期监听函数里做了什么

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

可以看出关键的函数在onActivityDestroyed监听onDestory()函数里
8.进入 ActivityRefWatcher.this.onActivityDestroyed(activity)

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

进入关键函数了,LeakCanary在每个Activity的生命周期onDestory函数里对activity对象进行监听,以此确实这个acitivity是否存在内存泄漏的问题。refWatcher就是刚才传入的RefWatcher实例。

9.进入到生成RefWatcher的LeakCanary.androidWatcher函数

 /**
   * Creates a {@link RefWatcher} with a default configuration suitable for Android.
   */
  public static RefWatcher androidWatcher(Context context, HeapDump.Listener heapDumpListener,
      ExcludedRefs excludedRefs) {
    LeakDirectoryProvider leakDirectoryProvider = new DefaultLeakDirectoryProvider(context);
    DebuggerControl debuggerControl = new AndroidDebuggerControl();
    AndroidHeapDumper heapDumper = new AndroidHeapDumper(context, leakDirectoryProvider);
    heapDumper.cleanup();
    Resources resources = context.getResources();
    int watchDelayMillis = resources.getInteger(R.integer.leak_canary_watch_delay_millis);
    AndroidWatchExecutor executor = new AndroidWatchExecutor(watchDelayMillis);
    return new RefWatcher(executor, debuggerControl, GcTrigger.DEFAULT, heapDumper,
        heapDumpListener, excludedRefs);
  }

androidWatcher函数在这里new出来了debuggerControl,heapDumper,executor这些对象,这些对象是做什么的,接下来有介绍,把这些参数传进new出来的RefWatcher的对象里,这个RefWatcher对象就是第8步里的refWatcher对象,接下来我们看看第8步里具体的refWatcher.watch执行过程

10.RefWatcher.watch函数,从这个函数来分析监听对象是否能被回收

/**
   * Identical to {@link #watch(Object, String)} with an empty string reference name.
   *
   * @see #watch(Object, String)
   */
  public void watch(Object watchedReference) {
    watch(watchedReference, "");
  }

  /**
   * Watches the provided references and checks if it can be GCed. This method is non blocking,
   * the check is done on the {@link Executor} this {@link RefWatcher} has been constructed with.
   *
   * @param referenceName An logical identifier for the watched object.
   */
  public void watch(Object watchedReference, String referenceName) {
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    if (debuggerControl.isDebuggerAttached()) {
      return;
    }
    final long watchStartNanoTime = System.nanoTime();
    String key = UUID.randomUUID().toString();
    retainedKeys.add(key);
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

    watchExecutor.execute(new Runnable() {
      @Override public void run() {
        ensureGone(reference, watchStartNanoTime);
      }
    });
  }

debuggerControl就是第9步new出来的AndroidDebuggerControl,其isDebuggerAttached()的实现就是判断是不是在连接了debug调试。如果连接了debug调试,则停止watch。
生成对一个Refercen产生一个唯一的key对象,系统nanoTime的watchStartNanoTime对象,并把key对象加入一个叫retainedKeys的全局Set集。并且将传入此函数的watchedReference,referenceName以及key,queue对象传入KeyedWeakReference生成一个名为reference的弱引用对象。
最后在线程池里运行ensureGone函数,ensureGone函数的参数是reference和watchStartNanoTime对象,线程池为第9步new出来的AndroidWatchExecutor对象

11.ensureGone函数

 void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();

    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    removeWeaklyReachableReferences();
    if (gone(reference) || debuggerControl.isDebuggerAttached()) {
      return;
    }
    gcTrigger.runGc();
    removeWeaklyReachableReferences();
    if (!gone(reference)) {
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);

      File heapDumpFile = heapDumper.dumpHeap();

      if (heapDumpFile == HeapDumper.NO_DUMP) {
        // Could not dump the heap, abort.
        return;
      }
      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
              gcDurationMs, heapDumpDurationMs));
    }
  }

函数首先调用removeWeaklyReachableReferences函数,移除retainedKeys中所有包含所有queue全局变量key的子项,然后使用gone函数判断引用对象是否能被gc,如果没有,使用gcTrigger.runGc()强制执行一边gc,在调用 removeWeaklyReachableReferences(),再次调用gone函数,让我们来看看removeWeaklyReachableReferences和gone函数的源码

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

为什么将queue里面的弱引用的key在retainedKeys移除,然后判断retainedKeys是否包含这个key就可以判断这个引用是否能被gc呢?
这是LeakCanary的核心原理,关键在于queue这个全局变量

  private final ReferenceQueue<Object> queue;
   public RefWatcher(Executor watchExecutor, DebuggerControl debuggerControl, GcTrigger gcTrigger,

    ......

    queue = new ReferenceQueue<>();
  }

第10步将queue作为KeyedWeakReference的构造参数传入,ReferenceQueue是作为 JVM GC与上层Reference对象管理之间的一个消息传递方式, 软引用、弱引用等的入队操作有vm的gc直接操作,retainedKeys 代表没被gc 回收的对象, 而queue中的弱引用代表的是被gc了的对象,通过这两个结构监控对象是不是被回收了。retainedKeys存放了RefWatcher为每个被监控的对象生成的唯一key。同时每个被监控对象的弱引用(KeyedWeakReference)关联了 其对应的key 和 queue,这样对象若被回收,则其对应的弱引用会被入队到queue中。removeWeaklyReachableReferences(..)所做的就是把存在与queue中的弱引用的key 从 retainedKeys 中删除。这样在gone函数中检测retainedKeys是否包含key就能检测出是否被gc了。

接下来,使用heapDumper.dumpHeap()产生一个dump文件,heapDumper为第9步生成的new AndroidHeapDumper的heapDumper参数其具体实现将Debug.dumpHprofData的数据保存到文件里。
接下来使用 heapdumpListener.analyze对dump文件进行分析,heapdumpListener为第一步里面的ServiceHeapDumpListener类。

12.进入ServiceHeapDumpListener.analyze函数

 @Override public void analyze(HeapDump heapDump) {
    checkNotNull(heapDump, "heapDump");
    HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
  }

进入到HeapAnalyzerService.runAnalysis中

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

调用HeapAnalyzerService解析dump文件,HeapAnalyzerService在另外一个进程空间里运行,来看AndroidManifest文件

   <service
        android:name=".internal.HeapAnalyzerService"
        android:process=":leakcanary"
        android:enabled="false"
        />

android:process表明其在单独的leakcanary进程运行

HeapAnalyzerService里的具体操作函数

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

heapAnalyzer.checkForLeak对其进行具体分析
AbstractAnalysisResultService.sendResultToListener将结果抛出。
12.进入heapAnalyzer.checkForLeak

 /**
   * Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key,
   * and then computes the shortest strong reference path from that instance to the GC roots.
   */
  public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
    long analysisStartNanoTime = System.nanoTime();

    if (!heapDumpFile.exists()) {
      Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
      return failure(exception, since(analysisStartNanoTime));
    }

    try {
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
      HprofParser parser = new HprofParser(buffer);
      Snapshot snapshot = parser.parse();
      deduplicateGcRoots(snapshot);

      Instance leakingRef = findLeakingReference(referenceKey, snapshot);

      // False alarm, weak reference was cleared in between key check and heap dump.
      if (leakingRef == null) {
        return noLeak(since(analysisStartNanoTime));
      }

      return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);
    } catch (Throwable e) {
      return failure(e, since(analysisStartNanoTime));
    }
  }

这一部分包含很多函数,主要是使用haha这库对dump文件进行分析,分析其中是否有还可达的对象,这一部分不在这里具体分析,想要弄清楚的童鞋去看源码吧。

13.当分析出结果后,使用AbstractAnalysisResultService.sendResultToListener抛出结果

 public static void sendResultToListener(Context context, String listenerServiceClassName,
      HeapDump heapDump, AnalysisResult result) {
    Class<?> listenerServiceClass;
    try {
      listenerServiceClass = Class.forName(listenerServiceClassName);
    } catch (ClassNotFoundException e) {
      throw new RuntimeException(e);
    }
    Intent intent = new Intent(context, listenerServiceClass);
    intent.putExtra(HEAP_DUMP_EXTRA, heapDump);
    intent.putExtra(RESULT_EXTRA, result);
    context.startService(intent);
  }

使用intent把结果抛给主进程,其中的listenerServiceClassName的service就是传入ServiceHeapDumpListener的DisplayLeakService.class的name

14.DisplayLeakService里对传过来的结果的处理函数

@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
    String leakInfo = leakInfo(this, heapDump, result, true);
    CanaryLog.d(leakInfo);

    boolean resultSaved = false;
    boolean shouldSaveResult = result.leakFound || result.failure != null;
    if (shouldSaveResult) {
      heapDump = renameHeapdump(heapDump);
      resultSaved = saveResult(heapDump, result);
    }

    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) {
      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;
    }
    showNotification(this, contentTitle, contentText, pendingIntent);
    afterDefaultHandling(heapDump, result, leakInfo);
  }

这个函数里主要是判断传来的结果有没有找到内存泄漏,如果找到的话把泄漏信息保存到文件,然后使用DisplayLeakActivity.createPendingIntent生成pendingIntent,然后发生通知到系统,DisplayLeakActivity.createPendingIntent里指明了点击通知会进入DisplayLeakActivity,DisplayLeakActivity会显示泄漏的一些具体信息。

LeakCanary的主要内容,到这里就分析完了,有什么错漏请大家批评指正。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值