源码分析:LeakCanary 如何实现内存泄露监测

作者: ImWiki | 来源:发表于2019-10-21 21:42 被阅读0次

    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

    相关文章

      网友评论

        本文标题:源码分析:LeakCanary 如何实现内存泄露监测

        本文链接:https://www.haomeiwen.com/subject/anvamctx.html