LeakCanary源码分析

来源:互联网 发布:java instanceof 编辑:程序博客网 时间:2024/06/14 06:07

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的主要内容,到这里就分析完了,有什么错漏请大家批评指正。

0 0