前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LeakCanary看这一篇文章就够了

LeakCanary看这一篇文章就够了

作者头像
用户1269200
发布2018-07-30 10:26:15
6.4K0
发布2018-07-30 10:26:15
举报
文章被收录于专栏:刘望舒刘望舒

作者:潇湘夜雨123 https://www.jianshu.com/p/8c06c9c9317c 本文由作者投稿并原创发布

LeakCanary简单介绍

LeakCanary是Square公司基于MAT开源的一个内存泄漏检测工具,在发生内存泄漏的时候LeakCanary会自动显示泄漏信息。

LeakCanary简单使用

配置build.gradle:

代码语言:javascript
复制
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
releaseImplementation'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'

自定义Application

代码语言:javascript
复制
public class ExampleApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        setupLeakCanary();
    }

    protected void setupLeakCanary() {
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }

如果当前的进程是用来给LeakCanary 进行堆分析的则return,否则会执行LeakCanary的install方法。这样我们就可以使用LeakCanary了,如果检测到某个Activity 有内存泄露,LeakCanary 就会给出提示。

LeakCanary原理

LeakCanary原理是将检测的对象放入弱引用中并且关联到引用队列中,查看引用队列中是否存在引用,如果发现泄露,dump出信息进行分析。 先从LeakCanary的install入手。

代码语言:javascript
复制
public static RefWatcher install(Application application) {
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
}

public static AndroidRefWatcherBuilder refWatcher(Context context) {
    return new AndroidRefWatcherBuilder(context);
}

install方法用于创建AndroidRefWatcherBuilder类,最终调用buildAndInstall方法。

代码语言:javascript
复制
public RefWatcher buildAndInstall() {
    if (LeakCanaryInternals.installedRefWatcher != null) {
      throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
    }
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      if (watchActivities) {
        ActivityRefWatcher.install(context, refWatcher);
      }
      if (watchFragments) {
        FragmentRefWatcher.Helper.install(context, refWatcher);
      }
    }
    LeakCanaryInternals.installedRefWatcher = refWatcher;
    return refWatcher;
  }

/** Creates a {@link RefWatcher}. */
public final RefWatcher build() {
    if (isDisabled()) {
      return RefWatcher.DISABLED;
}

buildAndInstall方法用于监听activity和fragment内存泄露,通过build方法创建RefWatcher从而创建ActivityRefWatcher和FragmentRefWatcher。

代码语言:javascript
复制
 public static void install(Context context, 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);
        }
};

首先进入监听activity类的ActivityRefWatcher类install中。 给Application注册ActivityLifecycleCallbacks,让activity中的onDestroy方法关联到refWatcher.watch中。 Application通过ActivityLifecycleCallbacks使用接口提供了一套回调方法, 对Activity的生命周期事件检测。ActivityLifecycleCallbacks使用要求API 14+ 。

代码语言:javascript
复制
 private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
        "com.squareup.leakcanary.internal.SupportFragmentRefWatcher";

    public static void install(Context context, RefWatcher refWatcher) {
      List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();

      if (SDK_INT >= O) {
        fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
      }

      try {
        Class<?> fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
        Constructor<?> constructor =
            fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
        FragmentRefWatcher supportFragmentRefWatcher =
            (FragmentRefWatcher) constructor.newInstance(refWatcher);
        fragmentRefWatchers.add(supportFragmentRefWatcher);
      } catch (Exception ignored) {
      }

      if (fragmentRefWatchers.size() == 0) {
        return;
      }

      Helper helper = new Helper(fragmentRefWatchers);

      Application application = (Application) context.getApplicationContext();
      application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
}

private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
        new ActivityLifecycleCallbacksAdapter() {
          @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            for (FragmentRefWatcher watcher : fragmentRefWatchers) {
              watcher.watchFragments(activity);
            }
          }
        };

再进入监听fragment的FragmentRefWatcher中,其内部类Helper将实现FragmentRefWatcher接口的SupportFragmentRefWatcher和AndroidOFragmentRefWatcher加入到fragmentRefWatchers数组中。 因为支持v4包下的fragment在另一个moudle中,所以通过反射的方式创建对象。 最后注册ActivityLifecycleCallbacks监听Activity的onCreated方法。

代码语言:javascript
复制
  private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
      new FragmentManager.FragmentLifecycleCallbacks() {
        @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);
  }

因为SupportFragmentRefWatcher和AndroidOFragmentRefWatcher是FragmentRefWatcher的实现类。最终会回调实现类的watchFragments方法,最终fragment会注册FragmentLifecycleCallbacks方法监听fragment。 activity和fragment最终都会调用RefWatcher中的watch方法,所以要先进入RefWatcher类中 看具体实现。

代码语言:javascript
复制
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 Set<String> retainedKeys;
  private final ReferenceQueue<Object> queue;
  private final HeapDump.Listener heapdumpListener;
  private final ExcludedRefs excludedRefs;
  private final boolean computeRetainedHeapSize;
}

WatchExecutor watchExecutor:查找检测内存泄露的对象 DebuggerControl debuggerControl:检测当前是否正在调试中 GcTrigger gcTrigger:调用gc方法 HeapDumper heapDumper:dump内存泄露产生的文件 SetretainedKeys:存储引用key(待检测或已经产生泄露) ReferenceQueue queue:引用队列,存储待检测的弱引用 HeapDump.Listener heapdumpListener:HeapDumper的回调 ExcludedRefs excludedRefs:排除系统引起的内存泄露 boolean computeRetainedHeapSize:检测泄露时是否计算堆的大小,默认为false

查看RefWatcher中的watch方法。

代码语言:javascript
复制
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);
  }

首先对retainedKeys添加一个唯一的key,并且将待检测的watchedReference加入转化为弱引用加入到引用队列中,开启异步线程ensureGoneAsync分析是否泄露。

代码语言:javascript
复制
  private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    watchExecutor.execute(new Retryable() {
      @Override public Retryable.Result run() {
        return ensureGone(reference, watchStartNanoTime);
      }
    });
  }

在ensureGoneAsync方法中,这里通过WatchExecutor接口的实现类AndroidWatchExecutor的,最后handler加入到消息队列中。

代码语言:javascript
复制
  @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);//gc时间到调用watch方法的时间差

    removeWeaklyReachableReferences();//清除到达引用队列中的弱引用

    if (debuggerControl.isDebuggerAttached()) {//判断是否为调试阶段
      // The debugger can create false leaks.
      return RETRY;
    }
    if (gone(reference)) {//判断检测对象是否达到引用队列
      return DONE;
    }
    gcTrigger.runGc();//调用gc回收
    removeWeaklyReachableReferences();//再次清除到达引用队列中的弱引用
       //再次判断检测对象是否达到引用队列如果未到达,产生heap文件。
    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.Durations durations =
          new HeapDump.Durations(watchDurationMs, gcDurationMs, heapDumpDurationMs);
      heapdumpListener.analyze(
          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs,
              computeRetainedHeapSize, durations));
    }
    return DONE;
  }

在ensureGone方法中,会清除引用队列中的弱引用,接着检测对象是否达到引用队列,然后调用gcTrigger.runGc进行gc回收,然后会再次清除引用队列中的弱引用。最终会调用heapdumpListener.analyze方法进行回调。

代码语言:javascript
复制
public final class ServiceHeapDumpListener implements HeapDump.Listener 
  @Override public void analyze(HeapDump heapDump) {
    checkNotNull(heapDump, "heapDump");
    HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
  }

heapdumpListener是HeapDump的内部接口。在ServiceHeapDumpListener进行实现然后会调用 HeapAnalyzerService.runAnalysis方法。

代码语言:javascript
复制
public final class HeapAnalyzerService extends ForegroundService
    implements AnalyzerProgressListener {
  public static void runAnalysis(Context context, HeapDump heapDump,
      Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    setEnabledBlocking(context, HeapAnalyzerService.class, true);
    setEnabledBlocking(context, listenerServiceClass, true);
    Intent intent = new Intent(context, HeapAnalyzerService.class);
    intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
    intent.putExtra(HEAPDUMP_EXTRA, heapDump);
    context.startService(intent);
  }

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

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

进入HeapAnalyzerService.runAnalysis方法可以看出会启动HeapAnalyzerService,HeapAnalyzerService是ForegroundService的子类。最终会调用onHandleIntentInForeground方法。 onHandleIntentInForeground方法会首先判断intent是否为空,然后调用heapAnalyzer.checkForLeak进行文件分析。最后调用AbstractAnalysisResultService.sendResultToListener进行回调显示结果。 进入HeapAnalyzer这个类中的checkForLeak方法 。

代码语言:javascript
复制
   public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey,
      boolean computeRetainedSize) {
    long analysisStartNanoTime = System.nanoTime();

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

    try {
      listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
       //将heap文件封装成MemoryMappedFileBuffer
      HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
      //创建hprof解析器,解析hprof文件
      HprofParser parser = new HprofParser(buffer);
      listener.onProgressUpdate(PARSING_HEAP_DUMP);
      Snapshot snapshot = parser.parse();//进行解析
      listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
      deduplicateGcRoots(snapshot);//去除重复的内容
      listener.onProgressUpdate(FINDING_LEAKING_REF);
       //检测解析的结果是否存在referenceKey的引用
      Instance leakingRef = findLeakingReference(referenceKey, snapshot);
      //根据leakingRef的结果调用noLeak或者寻找路径
      // 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, computeRetainedSize);
    } catch (Throwable e) {
      return failure(e, since(analysisStartNanoTime));
    }
  }

首先将heap文件封装成HprofParser,然后将hprof解析成内存快照,并且去掉重复的引用。最后根据内存快照找出泄露对象的最短路径。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-06-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 刘望舒 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • LeakCanary简单介绍
  • LeakCanary简单使用
  • LeakCanary原理
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档