LeakCanary 以1.5版本为例子,简单分析其中的原理。 LeakCanary 可以检测App的内存泄漏,在我们自定义的 Application 的 onCreate() 方法中执行  LeakCanary.install(this); 这行代码即可。代码很简单,我们看看它做了什么

  public static RefWatcher install(Application application) {return refWatcher(application).listenerServiceClass(DisplayLeakService.class).excludedRefs(AndroidExcludedRefs.createAppDefaults().build()).buildAndInstall();}

这个方法前两行是作一些参数配置,重点看 buildAndInstall() 方法

  public RefWatcher buildAndInstall() {RefWatcher refWatcher = build();if (refWatcher != DISABLED) {LeakCanary.enableDisplayLeakActivity(context);ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);}return refWatcher;}

通过  build() 创建一个 RefWatcher 对象,这是个build模式,看看它的代码

 public final RefWatcher build() {if (isDisabled()) {return RefWatcher.DISABLED;}ExcludedRefs excludedRefs = this.excludedRefs;if (excludedRefs == null) {excludedRefs = defaultExcludedRefs();}HeapDump.Listener heapDumpListener = this.heapDumpListener;if (heapDumpListener == null) {heapDumpListener = defaultHeapDumpListener();}DebuggerControl debuggerControl = this.debuggerControl;if (debuggerControl == null) {debuggerControl = defaultDebuggerControl();}HeapDumper heapDumper = this.heapDumper;if (heapDumper == null) {heapDumper = defaultHeapDumper();}WatchExecutor watchExecutor = this.watchExecutor;if (watchExecutor == null) {watchExecutor = defaultWatchExecutor();}GcTrigger gcTrigger = this.gcTrigger;if (gcTrigger == null) {gcTrigger = defaultGcTrigger();}return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,excludedRefs);}

其中 DebuggerControl 对应 AndroidDebuggerControl,HeapDumper 对应 AndroidHeapDumper,WatchExecutor 对应 AndroidWatchExecutor,GcTrigger 对应它内部的DEFAULT 对象,这几个类都比较简单,但比较有意思。

第一个就不说了,看 AndroidHeapDumper,这里面有几个知识点:FutureResult 的成员变量 AtomicReference 和 CountDownLatch,这是为了并发而准备的,CountDownLatch 是一个线程等待其他线程各自执行完毕后再执行本身,AtomicReference 则是保证原子性的安全;在showToast()方法中设置toast时,这里用了Looper队列MessageQueue的 addIdleHandler() 方法,它是UI空闲时执行,返回值为false则表示执行一次,true则是UI一旦空闲,就执行,没次数限制。
AndroidWatchExecutor 类是个执行功能类,它的构造方法中创建一个 HandlerThread 的子线程,然后把它对应的Looper作为参数,传入Handler的构造方法中,此时这个Handler就一直是在子线程中执行各种逻辑了,它里面通过代码执行延迟操作。 GcTrigger 这个里面执行的是GC操作, Runtime.getRuntime().gc() 比着 System.gc() 的优先级要高,所以优先使用这个。

继续回到 buildAndInstall() 方法,LeakCanary.enableDisplayLeakActivity(context) 的意思是打开显示内存泄漏的页面,这个页面把泄漏的数据以列表的形式展示出来;ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher) 这行代码则是对Activity执行监听的入口,看看对应的方法

  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();}public void watchActivities() {// Make sure you don't get installed twice.stopWatchingActivities();application.registerActivityLifecycleCallbacks(lifecycleCallbacks);}public void stopWatchingActivities() {application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);}

首先是个版本判断,要大于或等于14版本,这里其实就是给 application 注册一个Activity的生命周期回调,注册之前先取消之前的,防止重复注册,看看 lifecycleCallbacks 的方法

  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =new Application.ActivityLifecycleCallbacks() {...@Override public void onActivityDestroyed(Activity activity) {ActivityRefWatcher.this.onActivityDestroyed(activity);}};void onActivityDestroyed(Activity activity) {refWatcher.watch(activity);}

在Activity销毁时,执行了 refWatcher.watch(activity),看看它里面的代码

  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);}private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {watchExecutor.execute(new Retryable() {@Override public Retryable.Result run() {return ensureGone(reference, watchStartNanoTime);}});}

通过 randomUUID() 方法创建随机的字符串,然后把它存入set中;KeyedWeakReference 是个然引用,这里面传入的也有 ReferenceQueue 队列,看到这里,是不是很熟悉,Glide 的图片缓存中的软引用缓存,也是用的这种技术。看到这里,了解软引用和队列知识点的童鞋,就可以猜测出下面的步骤了

  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);heapdumpListener.analyze(new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,gcDurationMs, heapDumpDurationMs));}return DONE;}

对象的引用,如果强引用还有链接,则软引用中对象也会一直存在,如果强引用的链接都被切断了,那么软引用中很容易就被gc回收了,并且会把它放入传入软引用构造中的队列里面,所以 removeWeaklyReachableReferences() 的意思就是检查队列中是否有对象,如果有,说明正常回收,没有内存泄漏,因为此时 Activity 是执行了onDestroy() 方法,如果有内存泄漏,它被别的对象持有,那么它是无法释放资源。 gone() 方法是判断key是否在set中,还是判断Activity是否被回收,如果没被回收,为了保险,这里执行了 gcTrigger.runGc(),也就是主动触发 gc 回收机制,睡眠100毫秒,重新执行 removeWeaklyReachableReferences() 和 gone() 方法,如果此时 Activity 还没被回收,那就说明是内存泄漏了,heapDumper 是 AndroidHeapDumper,在它里面去读取对象的引用路径等信息。读取分析数据用的是 haha 三方库。

在 LeakCanary 1.6 版本中,有对 Fragment 的监听:在手机系统O版本以上,Fragment功能中也新增了对它的声明周期监听回调,

  public @NonNull RefWatcher buildAndInstall() {if (LeakCanaryInternals.installedRefWatcher != null) {throw new UnsupportedOperationException("buildAndInstall() should only be called once.");}RefWatcher refWatcher = build();if (refWatcher != DISABLED) {LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);if (watchActivities) {ActivityRefWatcher.install(context, refWatcher);}if (watchFragments) {FragmentRefWatcher.Helper.install(context, refWatcher);}}LeakCanaryInternals.installedRefWatcher = refWatcher;return refWatcher;}AndroidOFragmentRefWatcher: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);}}@Overridepublic void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {refWatcher.watch(fragment);}};@Override public void watchFragments(Activity activity) {FragmentManager fragmentManager = activity.getFragmentManager();fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);}

这里是根据声明周期,把 rootview 和 fragment 添加到 RefWatcher 的监听方法中,关键就是 watchFragments(Activity activity) 方法是如何触发的,继续看 FragmentRefWatcher,创建了 Helper 对象,把 fragmentRefWatchers 作为参数传入构造方法中,用 ActivityLifecycleCallbacks 包裹了一层,ActivityLifecycleCallbacks 被添加到Application 的回调中,activity销毁时执行 ActivityLifecycleCallbacks 的 onActivityCreated() 方法,此时就触发了 AndroidOFragmentRefWatcher 的 watchFragments()方法。

如果我们想对其他类型的对象进行内存泄漏检测怎么办?Application中的 LeakCanary.install(this) 返回的是 RefWatcher 对象,此时可以做一个单例的Application对象,对外暴露 RefWatcher,这样其他地方就可以直接使用 RefWatcher 的 watch(Object watchedReference) 方法了,但这里需要自己控制时机,因为这里不会根据Activity的声明周期来触发。

LeakCanary 的2.0版本,做了部分修改,使用它时不需要手动LeakCanary.install(this)初始化了,应为它自己做了。里面有个继承 ContentProvider 的内容观察者,观察者的初始化方法居然比 Application 的初始化方法还要早;用了新的内存解析库代替了haha库,极大的减少了内存占用,提高了n被的速度。

LeakCanary 原理分析相关推荐

  1. Android LeakCanary原理分析

    概述 在上一篇LeakCanary使用详细教程中,我们熟悉了LeakCanary的使用和初步描述了它的工作机制,这篇我准备从源码的角度去分析LeakCanary的工作原理: 源码分析 从上一篇中我们知 ...

  2. Leakcanary原理解析以及换肤框架skin的原理分析

    一.错误现场 java.lang.ClassCastException: androidx.appcompat.widget.TintContextWrapper cannot be cast to ...

  3. leakCanary原理

    一.面试中的问题 一般的中高级面试,都会问到性能优化,内存优化问题,而说到内存问题就肯定会问到内存泄漏问题,而一般的求职者二话不说,直接就上LeakCanary, 紧接着肯定是问:那你知道LeakCa ...

  4. java signature 性能_Java常见bean mapper的性能及原理分析

    背景 在分层的代码架构中,层与层之间的对象避免不了要做很多转换.赋值等操作,这些操作重复且繁琐,于是乎催生出很多工具来优雅,高效地完成这个操作,有BeanUtils.BeanCopier.Dozer. ...

  5. Select函数实现原理分析

    转载自 http://blog.chinaunix.net/uid-20643761-id-1594860.html select需要驱动程序的支持,驱动程序实现fops内的poll函数.select ...

  6. spring ioc原理分析

    spring ioc原理分析 spring ioc 的概念 简单工厂方法 spirng ioc实现原理 spring ioc的概念 ioc: 控制反转 将对象的创建由spring管理.比如,我们以前用 ...

  7. 一次 SQL 查询优化原理分析(900W+ 数据,从 17s 到 300ms)

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:Muscleape jianshu.com/p/0768eb ...

  8. 原理分析_变色近视眼镜原理分析

    随着眼镜的发展,眼镜的外型变得越来越好看,并且眼镜的颜色也变得多姿多彩,让佩戴眼镜的你变得越来越时尚.变色近视眼镜就是由此产生的新型眼镜.变色镜可以随着阳光的强弱变换不同的色彩. 变色眼镜的原理分析 ...

  9. jieba分词_从语言模型原理分析如何jieba更细粒度的分词

    jieba分词是作中文分词常用的一种工具,之前也记录过源码及原理学习.但有的时候发现分词的结果并不是自己最想要的.比如分词"重庆邮电大学",使用精确模式+HMM分词结果是[&quo ...

最新文章

  1. awk数组命令经典生产实战应用拓展
  2. 【BZOJ3745】Norma(CDQ分治)
  3. 计算机应用基础作业北语,北语计算机应用基础作业.doc
  4. UA MATH567 高维统计IV Lipschitz组合3 高斯分布的Lipschitz函数
  5. C#启动其他程序的代码
  6. Linux环境 安装 Redis-6.2.6 配置运行_01
  7. c++获取输入数字的位数/获取位数并且将其存入数组中/获取位数存入数组并且利用它解决实际问题
  8. 利用Markdown编写美妙的数学公式
  9. 拳王虚拟项目公社:2020考研专业院校选择
  10. java用输入流创建数据文件_java开发知识IO知识之输入输出流以及文件
  11. axios.js post 后台木有数据
  12. 主成分分析和因子分析_简单易懂!一文理清主成分分析思路
  13. soapui 使用 java_SoapUI的进阶使用
  14. html英文特殊字体代码,字体_中英文字体等(示例代码)
  15. PS-tenday-强大的画笔工具(手绘)
  16. 怎样登陆微信公众号?怎样登陆微信公众号后台?
  17. 什么是L2行情接口?
  18. 大脑的终极秘密——从狮子也有意识谈起
  19. layui外部引入_layui use 定义js外部引用函数的方法
  20. Verilog基础知识

热门文章

  1. 使用Burp Suite对登录页面进行字典攻击
  2. 『Java安全』反序列化-CC7反序列化漏洞POP链分析_ysoserial CommonsCollections7 PoC分析
  3. 转载GitHub28.7k星的更好地提问ChatGPT方法
  4. PPP学习---天线相位中心改正文件格式学习
  5. 急性喉炎发病四个主要原因
  6. dw网页设计期末设计一个网页_Dreamweaver网页设计模拟试题
  7. 思科交换机65系列配置
  8. 湖南计算机专业好的二本学校排名2015,2015湖南二本大学排名
  9. 【linux驱动】网卡驱动程序
  10. 希捷酷鱼 7200.12 500GB (16MB Cache)硬盘亲身体验