LeakCanary 源码分析

目录

问题

1.LeakCanary 是如何实现内存泄漏检测的?

2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?

3.什么时候(时机)会进行内存泄漏检查?

4.内存快照文件是怎么生成的?

一、版本

二、类图

三、流程

3.1 LeakCanary初始化流程 + 配置Activity泄漏监听(Fragment泄漏监听)

3.2对象观察流程

3.3dump内存文件+泄漏分析流程

四、问题

1.LeakCanary 是如何实现内存泄漏检测的?

2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?

3.什么时候(时机)会进行内存泄漏检查?

4.内存快照文件是怎么生成的?

五、总结

 

 

 

六、学习到了什么

 

 

 

 

七、参考


思考问题

1.LeakCanary 是如何实现内存泄漏检测的?

2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?

3.什么时候(时机)会进行内存泄漏检查?

4.内存快照文件是怎么生成的?

 

一、版本

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
releaseImplementation   'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
// 可选,如果你使用支持库的fragments的话
debugImplementation   'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'

二、类图

三、流程

3.1 LeakCanary初始化流程 + 配置Activity泄漏监听(Fragment泄漏监听)

public class WanAndroidApp extends Application {

    private RefWatcher refWatcher;

    public static RefWatcher getRefWatcher(Context context) {
        WanAndroidApp application = (WanAndroidApp)     context.getApplicationContext();
        return application.refWatcher;
    }

    @Override public void onCreate() {
      super.onCreate();
      //如果在内存分析进程中,则返回
      if (LeakCanary.isInAnalyzerProcess(this)) {
        return;
      }
      //主进程进行初始化
      refWatcher = LeakCanary.install(this);
    }
}

====
LeakCanary.java

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

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

====
AndrdoidRefWatcherBuilder.java

  /**
   * Sets a custom {@link AbstractAnalysisResultService} to listen to analysis results. This
   * overrides any call to {@link #heapDumpListener(HeapDump.Listener)}.
   */
  public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
      @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
    //创建一个内存dump的回调监听
    return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
  }

  public @NonNull RefWatcher buildAndInstall() {
    if (LeakCanaryInternals.installedRefWatcher != null) {
      throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
    }
    //创建一个默认的RefWatcher (补充4)
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      //设置DisplayLeakActivity.class组件可用
      LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
      if (watchActivities) {
        //添加Activity泄漏的监听 (补充2)
        ActivityRefWatcher.install(context, refWatcher);
      }
      if (watchFragments) {
        //添加Fragment泄漏的监听 (补充3)
        FragmentRefWatcher.Helper.install(context, refWatcher);
      }
    }

    //设置LeakCanaryInternals内部观察对象
    LeakCanaryInternals.installedRefWatcher = refWatcher;
    return refWatcher;
  }

补充1

  /**
   * This returns the references in the leak path that can be ignored for app developers. This
   * doesn't mean there is no memory leak, to the contrary. However, some leaks are caused by bugs
   * in AOSP or manufacturer forks of AOSP. In such cases, there is very little we can do as app
   * developers except by resorting to serious hacks, so we remove the noise caused by those leaks.
   */
  public static @NonNull ExcludedRefs.Builder createAppDefaults() {
    //排除Android系统泄漏和厂商泄漏的Case集合
    return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
  }

  public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet<AndroidExcludedRefs> refs) {
    ExcludedRefs.Builder excluded = ExcludedRefs.builder();
    for (AndroidExcludedRefs ref : refs) {
      //根据枚举类里面的applies属性,排除的系统泄漏和厂商的泄漏case
      if (ref.applies) {
        ref.add(excluded);
        ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
      }
    }
    return excluded;
  }

补充2

ActivityRefWatcher.java


  public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
    Application application = (Application) context.getApplicationContext();
    ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);

//注册Activity生命周期回调    application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
  }


  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
          //当activity回调onDestroyed()时,使用refWatcher进行观察
          refWatcher.watch(activity);
        }
      };

补充3

FragmentRefWatcher.java


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

      if (SDK_INT >= O) {
        //添加Android O版本的Fragment
        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);
        //使用反射添加support库的Fragment
        fragmentRefWatchers.add(supportFragmentRefWatcher);
      } catch (Exception ignored) {
      }

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

      Helper helper = new Helper(fragmentRefWatchers);

      Application application = (Application) context.getApplicationContext();
//注册Activity生命周期回调
application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
    }



    private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
        new ActivityLifecycleCallbacksAdapter() {
          @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            //当Activity回调onCreate()时,遍历fragment观察集合
            for (FragmentRefWatcher watcher : fragmentRefWatchers) {
              watcher.watchFragments(activity);
            }
          }
        };

====
SupportFragmentRefWatcher.java

  @Override public void watchFragments(Activity activity) {
    if (activity instanceof FragmentActivity) {
      FragmentManager supportFragmentManager =
          ((FragmentActivity) activity).getSupportFragmentManager();
//注册activity对应的fragment的生命周期回调
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
    }
  }

  private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
      new FragmentManager.FragmentLifecycleCallbacks() {

        @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
          View view = fragment.getView();
          if (view != null) {
            //当Fragment回调onViewDestroyed()时,进行refWatcher.watch()观察泄漏
            refWatcher.watch(view);
          }
        }

        @Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
          //当Fragment回调onDestroyed()时,进行refWatcher.watch()观察泄漏
          refWatcher.watch(fragment);
        }
      };

补充4:由于Builder使用的是AndroidRefWatcherBuilder对象,默认的创建对象进行了重写,比如Executor,DebugControl...

RefWatcherBuilder.java

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

    //前面我们设置了排除Android原生和厂商泄漏的Case,所以excludedRefs != null
    if (heapDumpBuilder.excludedRefs == null) {
      heapDumpBuilder.excludedRefs(defaultExcludedRefs());
    }

    HeapDump.Listener heapDumpListener = this.heapDumpListener;
    if (heapDumpListener == null) {
      //设置内存dump回调监听
      heapDumpListener = defaultHeapDumpListener();
    }

    //设置默认Debug控制器AndroidDebuggerControl,用来在用户Debug时,不进行内存观察!
    DebuggerControl debuggerControl = this.debuggerControl;
    if (debuggerControl == null) {
      debuggerControl = defaultDebuggerControl();
    }

    HeapDumper heapDumper = this.heapDumper;
    if (heapDumper == null) {
      //设置默认内存dumper对象AndroidHeapDumper
      heapDumper = defaultHeapDumper();
    }

    WatchExecutor watchExecutor = this.watchExecutor;
    if (watchExecutor == null) {
      //设置默认线程池AndroidWatchExecutor
      watchExecutor = defaultWatchExecutor();
    }

    GcTrigger gcTrigger = this.gcTrigger;
    if (gcTrigger == null) {
      //设置默认的GcTrigger
      gcTrigger = defaultGcTrigger();
    }

    if (heapDumpBuilder.reachabilityInspectorClasses == null) {
      heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
    }

    return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
        heapDumpBuilder);
  }

 

3.2对象观察流程

在3.1最后一步的流程中,build()方法创建了一个RefWatcher对象

final class RefWatcher //最终类,不能继承

  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 WatchExecutor} this {@link RefWatcher} has been constructed
   * with.
   *
   * @param referenceName An logical identifier for the watched object.
   */
  public void watch(Object watchedReference, String referenceName) {
    if (this == DISABLED) {
      return;
    }
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    final long watchStartNanoTime = System.nanoTime();
    //生成一个唯一值的id
    String key = UUID.randomUUID().toString();
    //添加到保存retainedKeys(Set)集合中
    retainedKeys.add(key);
    //创建一个弱引用对象,并且关联queue弱引用队列 (补充1)
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);

    ensureGoneAsync(watchStartNanoTime, reference);
  }

  private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    //AndroidWatchExecutor中将Retryable放入到主线程的IdleHandler进行执行
    watchExecutor.execute(new Retryable() {
      @Override public Retryable.Result run() {
        //主线程执行
        return ensureGone(reference, watchStartNanoTime);
      }
    });
  }

  Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    long gcStartNanoTime = System.nanoTime();
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

    //取出弱引用队列对象,并对retainKeys集合进行移除
    removeWeaklyReachableReferences();

    //如果项目正在debug时,返回RETRY结果,在AndroidWatchExecutor中延后执行
    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    //如果retainedKeys集合不包含该弱引用对象了,则表示对象已经被回收了,不存在泄漏
    if (gone(reference)) {
      return DONE;
    }
    //主动触发一次GC回收
    gcTrigger.runGc();
    //再一次移除retainKeys集合弱引用对象
    removeWeaklyReachableReferences();
    //如果retainedKeys集合包含该弱引用对象,则存在泄漏
    if (!gone(reference)) {
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
    
      //dump内存文件
      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 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;
    //循环遍历,将弱引用队列中的对象弹出,并将retainKeys集合中进行去除
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {
      retainedKeys.remove(ref.key);
    }
  }

  private boolean gone(KeyedWeakReference reference) {
    //如果retainedKeys集合不包含该弱引用对象了,则表示对象已经被回收了,不存在泄漏
    return !retainedKeys.contains(reference.key);
  }

补充1

final class KeyedWeakReference extends WeakReference<Object> {

  //继承WeakReference类,
  //有核心功能:当弱引用被GC回收时,会把弱引用对象加入到关联的队列中
  KeyedWeakReference(Object referent, String key, String name,
      ReferenceQueue<Object> referenceQueue) {
    super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
    this.key = checkNotNull(key, "key");
    this.name = checkNotNull(name, "name");
  }

}

3.3dump内存文件+泄漏分析流程

AndroidHeapDumper.java

  public File dumpHeap() {
    File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();

    if (heapDumpFile == RETRY_LATER) {
      return RETRY_LATER;
    }

    FutureResult<Toast> waitingForToast = new FutureResult<>();
    showToast(waitingForToast);

    if (!waitingForToast.wait(5, SECONDS)) {
      CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
      return RETRY_LATER;
    }

    Notification.Builder builder = new Notification.Builder(context)
        .setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
    Notification notification = LeakCanaryInternals.buildNotification(context, builder);
    NotificationManager notificationManager =
        (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    int notificationId = (int) SystemClock.uptimeMillis();
    notificationManager.notify(notificationId, notification);

    Toast toast = waitingForToast.get();
    try {
      //核心代码:dump内存文件的API
      Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
      cancelToast(toast);
      notificationManager.cancel(notificationId);
      return heapDumpFile;
    } catch (Exception e) {
      CanaryLog.d(e, "Could not dump heap");
      // Abort heap dump
      return RETRY_LATER;
    }
  }

====
ServiceHeapDumpListener.java

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

====
HeapAnalyzerService.java

  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);
    //启动前台服务
    ContextCompat.startForegroundService(context, 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, heapDump.reachabilityInspectorClasses);

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

四、问题

1.LeakCanary 是如何实现内存泄漏检测的?

答:

底层原理:

将弱引用(WeakReference)和引用队列(ReferenceQueue)关联起来

如果弱引用持有的对象被GC回收了,JVM就会把这个弱引用对象加入到与之关联的引用队列中

 

2.Fragment是如何进行内存泄漏检测的? 泄漏的判断标准是什么?

答:
1.Fragment对象是通过注册activity里面的fragmentManager或者SupportFragmentManager对Fragment的生命周期onViewDetoryed(),onDestroyed()回调进行检测的

2.判断的标准是,如果fragment调用了生命周期onViewDetoryed(),onDestroyed(),而在retainedKeys集合中,还存在的话,说明Fragment泄漏了

 

3.什么时候(时机)会进行内存泄漏检查?

答:

在AndroidWatchExecutor中将Retryable放入到主线程的IdleHandler进行执行,将内存检测方法推迟到了主线程的Handler Looper闲置时进行检查!

 

4.内存快照文件是怎么生成的?

答:

通过调用 Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); //会写入到指定文件中

 

五、总结

 
优点

1.DebugImplement实现,可以在研发流程中观察到内存泄漏问题

 2.优秀的内存泄漏结果展示
  
缺点1.由于对内存dump时会非常耗时,并且停住主线程,所以不能用于线上环境

 

 

 

 

六、学习到了什么

设计模式

组合模式

RefWatcher是一个final class, 是检测泄漏方法的核心类, 同时拥有内存泄漏的通用发放,

AndroidRefWatcher,FragmentRefWatcher引用RefWatcher对象来实现对Activity,Fragment对象的内存泄漏监听
使用KOOM, KOOMInternal2个类,外部暴露KOOM类给用户进行配置,

内部使用用KOOMInternal类进行实现总体流程的控制

 

构造者模式
AndroidRefWatcherBuilder

RefWatcherBuilder 进行对实体对象进行创建

  
设计1.在AndroidWatchExecutor中将Retryable放入到主线程的IdleHandler进行执行,将内存检测方法推迟到了主线程的Handler Looper闲置时进行检查!
 

2.底层原理:

将弱引用(WeakReference)和引用队列(ReferenceQueue)关联起来

如果弱引用持有的对象被GC回收了,JVM就会把这个弱引用对象加入到与之关联的引用队列中

 

 

 

 

七、参考

https://jsonchao.github.io/2019/01/06/Android%E4%B8%BB%E6%B5%81%E4%B8%89%E6%96%B9%E5%BA%93%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E5%85%AD%E3%80%81%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Leakcanary%E6%BA%90%E7%A0%81%EF%BC%89/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值