阿里一面之LeakCanary内存泄漏监测原理总结

本文通过在阿里面试遇到的问题总结而出,如有不对地方,请及时批评指正。篇幅较长,请耐心阅读。

简介:

LeakCanary是一个开源的内存泄漏检查工具,使用简单,主要用来监测Activity和Fragment是否发生内存泄漏。如果发生内存泄漏,直接以引用链的形式展示出造成内存泄漏对象。

使用步骤

  1. 添加build.gradle依懒。
  debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
  releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
  debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'

2.在Application中初始化LeakCanary。

class CustomApplication:MainApplication() {
    override fun onCreate() {
        super.onCreate()
       if (! LeakCanary.isInAnalyzerProcess(this)) {
            LeakCanary.install(this);
        }
    }
}


完成以上两步操作之后,当打开app运行时,如果Activity或Fragment发生内存泄漏,会以通知的形式提醒用户。

源码分析

先通过流程图来看一下LeakCanary工作原理,如图所示:

LeakCanary初始化

1.在application中注册。

class CustomApplication:MainApplication() {
    override fun onCreate() {
        super.onCreate()
       if (! LeakCanary.isInAnalyzerProcess(this)) {
           //注册leakcanary
            LeakCanary.install(this);
        }
    }
}

2.构建观察者RefWatcher。

public static RefWatcher install(Application application) {
    //构建观察者RefWatcher
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
}

3.构建不同的观察者ActivityRefWatcher或FragmentRefWatcher。

public RefWatcher buildAndInstall() {
    if (LeakCanaryInternals.installedRefWatcher != null) {
      throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
  }
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
        //观察activity
      if (watchActivities) {
        ActivityRefWatcher.install(context, refWatcher);
    }
      //观察fragment
      if (watchFragments) {
        FragmentRefWatcher.Helper.install(context, refWatcher);
    }
  }
    LeakCanaryInternals.installedRefWatcher = refWatcher;
    return refWatcher;
}

4.监测activity生命周期。

public static void install(Context context, RefWatcher refWatcher) {
    Application application = (Application) context.getApplicationContext();
    ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
   //注册生命周期
    application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}

5.将观察对象activity添加到被观察者队列。

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
    new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
            //在activity的onDestroy生命周期中添加观察对象
          refWatcher.watch(activity);
      }
    };

泄漏检测

1.将被观察对象包装成弱引用。

public void watch(Object watchedReference, String referenceName) {
    if (this == DISABLED) {
      return;
  } 
    //判空检查
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    final long watchStartNanoTime = System.nanoTime();
    //生成唯一key
    String key = UUID.randomUUID().toString();
    //保存key
    retainedKeys.add(key);
    //包装成弱引用对象
    final KeyedWeakReference reference =
    new KeyedWeakReference(watchedReference, key, referenceName, queue);
    //检查被观察对象是否被回收
    ensureGoneAsync(watchStartNanoTime, reference);
}

2.检测弱引用对象是否被回收。

@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);
    //移除被回收对象的key
    removeWeaklyReachableReferences();

    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
  }
    //判断弱引用是否被回收
    if (gone(reference)) {
      return DONE;
  }
    //触发系统GC进行垃圾回收
    gcTrigger.runGc();
    //再次移除被回收对象的key
    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;
}

内存分析

1.启动HeapAnalyzerService进行内存分析

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

2.保存文件,并发送通知给用户

@Override protected final void onHeapAnalyzed(HeapDump heapDump, AnalysisResult result) {
    String leakInfo = leakInfo(this, heapDump, result, true);
    CanaryLog.d("%s", 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) {
        if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
          String className = classSimpleName(result.className);
          if (result.excludedLeak) {
            contentTitle = getString(R.string.leak_canary_leak_excluded, className);
        } else {
            contentTitle = getString(R.string.leak_canary_class_has_leaked, className);
        }
      } else {
          String size = formatShortFileSize(this, result.retainedHeapSize);
          String className = classSimpleName(result.className);
          if (result.excludedLeak) {
            contentTitle = getString(R.string.leak_canary_leak_excluded_retaining, className, size);
        } else {
            contentTitle =
            getString(R.string.leak_canary_class_has_leaked_retaining, 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;
  }
    // 每次发送一个新通知提醒用户。
    int notificationId = (int) (SystemClock.uptimeMillis() / 1000);
    showNotification(this, contentTitle, contentText, pendingIntent, notificationId);
    afterDefaultHandling(heapDump, result, leakInfo);
}

整个监测过程主要作用如下:

1.注册监听activity生命周期。

2.在activity被销毁时加入弱引用队列。

3.第一次移除不可达对象,移除ReferenceQueue中的KeyedWeakReference。

4.主动触发GC进行垃圾回收。

5.第二次移除不可达对象,移除ReferenceQueue中的KeyedWeakReference。

6.判断当前是否还有对象存活,如果有保存存活对象的内存快照heapDumpFile,然后进行内存分析。

7.启动HeapAnalyzerService对内存快照进行分析,找出GCroots引用链。

8.发送通知给用户。


以上就是阿里面试后总结的几个要点,还不会的同学赶紧学起来吧,感谢您的阅读,创造不易,如果您觉得本篇文章对您有帮助,请点击关注小编,您的支持就是小编创作的最大动力!

展开阅读全文

页面更新:2024-04-30

标签:阿里   内存   观察者   快照   队列   生命周期   原理   对象   发生   通知   用户

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top