Android LeakCanary检测内存泄露原理

以LeakCanary2.6源码分析LeakCanary检测内存泄露原理,为减少篇幅长度,突出关键点,不粘贴大量源码,阅读时需搭配源码食用。

如何获取context

LeakCanary只需引入依赖,不需要初始化代码,就能执行内存泄漏检测了,它是通过ContentProvider获取应用的context。这种获取context方式在开源第三方库中十分流行。如下AppWatcherInstaller在LeakCanary的aar包中manifest文件中注册。

internal sealed class AppWatcherInstaller : ContentProvider() {
 override fun onCreate(): Boolean {
  val application = context!!.applicationContext as Application
  AppWatcher.manualInstall(application)//1
  return true
 }
 ...
}

默认检测哪些类对象的内存泄露

(1)处的方法将调用如下方法注册需要检测泄露的对象:

 fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher
 ): List<InstallableWatcher> {
  return listOf(
   ActivityWatcher(application, reachabilityWatcher),
   FragmentAndViewModelWatcher(application, reachabilityWatcher),
   RootViewWatcher(reachabilityWatcher),
   ServiceWatcher(reachabilityWatcher)
  )
 }

可以看到LeakCanary会把Activity,Fragment,ViewModel,RootView和Service纳入检测,这些对象都是有明确的生命周期,而且占用内存较高,它们的内存泄露是需要我们重点关注的。

如何将这些生命周期对象纳入监测

(1)处的manualInstall方法将遍历调用上述Watcher的install方法以适时将这些生命周期对象纳入检测。

ActivityWatcher

ActivityWatcher中install方法通过向application注册Application.ActivityLifecycleCallbacks接口回调实现对Activity生命周期的检测。这里有一个很棒的技巧,利用Kotlin委托与Java动态代理,将不需要关注的方法给出默认空实现,(2)(3)处代码提取出来,可以在平时开发中有需求的地方使用。

//ActivityWatcher
 private val lifecycleCallbacks =
  object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
   override fun onActivityDestroyed(activity: Activity) {
    reachabilityWatcher.expectWeaklyReachable(
     activity, "${activity::class.java.name} received Activity#onDestroy() callback"
    )//4
   }
  }

internal inline fun <reified T : Any> noOpDelegate(): T {
 val javaClass = T::class.java
 return Proxy.newProxyInstance(
  javaClass.classLoader, arrayOf(javaClass), NO_OP_HANDLER
 ) as T
}//2

private val NO_OP_HANDLER = InvocationHandler { _, _, _ ->
 // no op
}//3

(4)调用的objectWatcher.expectWeaklyReachable方法是将对象纳入监测的通用方法,如其名称所示,WeaklyReachable相较的是StronglyReachable,当一个对象不再需要时,我们希望它从WeaklyReachable变为StronglyReachable。

我们可以在不再需要某对象时主动调用该方法,检测任意对象(除上节的默认对象)的内存泄露:

AppWatcher.objectWatcher.expectWeaklyReachable(obj, "")

onActivityDestroyed回调中就通过该方式将activity纳入监测。

通过上述对Activity的纳入内存泄露源码的分析,可以发现其中2个关键点,首先需要能获取应用中所有待检测对象的引用,其次需要一个待检测对象生命周期结束的时机。而这两点通过注册Application.ActivityLifecycleCallbacks接口能够同时满足,可对于其他类对象,就没有如此便捷的方式了。

下面介绍Fragment,ViewModel,RootView和Service这些类对象是如何纳入检测的。

FragmentAndViewModelWatcher

Fragment为了兼容在Android源码中几个不同包名的实现,对它们的检测也需要分别实现,我们在FragmentAndViewModelWatcher中只关注AndroidXFragmentDestroyWatcher对AndroidX中Fragment的内存泄露检测即可,其他几个实现类似。

FragmentAndViewModelWatcher先同样通过注册Application.ActivityLifecycleCallbacks回调,适时获取Activity引用,并在AndroidXFragmentDestroyWatcher获取Activity的supportFragmentManager,向其注册FragmentManager.FragmentLifecycleCallbacks。在其中的onFragmentDestroyed与onFragmentViewDestroyed回调中将Fragment和Fragment的View纳入内存泄露检测。

对于ViewModel的检测,则需要关注ViewModelClearedWatcher,通过用上一步获取的Activity引用,添加名为ViewModelClearedWatcher的spy ViewModel,来获得收到onCleared回调的能力,因为对于一个ViewModelStoreOwner(Activity,Fragment)来说,自己的一个ViewModel回调了onCleared,则其他ViewModel的onCleared也应该被调用。这些ViewModel是通过ViewModelStore的mMap属性反射获取的。在spy ViewModel的onCleared回调中,纳入内存泄露检测。

RootViewWatcher

对于Android里Window中的RootView,即DecorView,可以通过注册addOnAttachStateChangeListener在View的onViewDetachedFromWindow时进行检测。而获取待检测对象的引用就不像Activity和Fragment一样有回调可以依赖了。LeakCanary采取了Hook的方式在install方法对RootView的容器进行替换,具体来说就是通过反射机制将WindowManagerGlobal中的mViews(包含所有Window中的DecorView)的ArrayList容器的实现修改,在其add方法中获取DecorView的引用,之后设置OnAttachStateChangeListener回调进行检测。

ServiceWatcher

而Android中Service,无论是获取引用还是监测时机的确定都没有系统的回调可以依赖,LeakCanary都是采用Hook的方式达到目的。首先通过反射拿到ActivityThread中的mServices,这是包含app中全部Service的一个Map。在install方法中有两个Hook点,首先是Android 消息机制的中转中心,名为H的Handler,系统侧对应用侧的全部回调都需要经过它的周转。因为Handler中mCallback执行的优先级大于handleMessage方法,Leakcanary替换H的mCallback实现,当消息为STOP_SERVICE时,便从mServices取出该消息对应的Service作为待检测Service引用。第二个Hook点为ActivityManagerService,通过动态代理修改它的serviceDoneExecuting方法,在其真正实现前增加内存泄露检测,其余方法保持不变。

这些类纳入检测纳入检测的时机,可总结为如下表格:

如何获取引用 何时纳入监测
Activity ActivityLifecycleCallbacks回调 onActivityDestroyed
Fragment FragmentLifecycleCallbacks回调 onFragmentDestroyed
Fragment中的View FragmentLifecycleCallbacks回调 onFragmentViewDestroyed
ViewModel 反射获取ViewModelStore的mMap spy ViewModel的onCleared
Window中的DecorView Hook WindowManagerGlobal中的mViews onViewDetachedFromWindow
Service Hook H的mCallback实现,当消息为STOP_SERVICE时,从ActivityThread中的mServices获取 Hook ActivityManagerService,serviceDoneExecuting中检测

如何确定内存泄露的对象

在确定待检测对象与时机后,查看ObjectWatcher的expectWeaklyReachable方法,可以得知如何实现将泄露对象从待检测对象(默认即上节我们分析的那些有生命周期的类对象)挑出来的。确定内存泄露对象的原理是我们常用的WeakReference,其双参数构造函数支持传入一个ReferenceQueue,当其关联的对象回收时,会将WeakReference加入ReferenceQueue中。LeakCanary的做法是继承ReferenceQueue,增加一个值为UUID的属性key,同时将每个需要监测的对象WeakReference以此UUID作为键加入一个map中。这样,在GC过后,removeWeaklyReachableObjects方法通过遍历ReferenceQueue,通过key值删除map中已回收的对象,剩下的对象就基本可以确定发生了内存泄露。

如何确定从GC root到泄露对象的引用链

在确定内存泄露的对象后,就需要其他手段来确定泄露对象引用链了,这一过程开始于checkRetainedObjects方法,跟踪调用可以看到启动了前台服务HeapAnalyzerService,这在我们使用LeakCanary时可以在通知栏看到。服务中调用了HeapAnalyzer的analyze方法进行堆内存分析,由Shark库实现该功能,就不再进行追踪。

以上就是分析LeakCanary检测内存泄露原理的详细内容,更多关于LeakCanary检测内存泄露的资料请关注我们其它相关文章!

(0)

相关推荐

  • Android Studio 3.0上分析内存泄漏的原因

    以前用eclipse的时候,我们采用的是DDMS和MAT,不仅使用步骤复杂繁琐,而且要手动排查内存泄漏的位置,操作起来比较麻烦.后来随着Android studio的潮流,我也抛弃了eclipse加入了AS. Android Studio也开始支持自动进行内存泄漏检查,并且操作起来也比较方便. 封面 戳我下载 Android Studio 3.0 这个不用梯子我会告诉你吗 1.写在前面 Google在上周发布了Android Studio 3.0的正式版本,周四早晨在上班的地铁上就看到群里在沸沸

  • 使用Android Studio检测内存泄露(LeakCanary)

    内存泄露,是Android开发者最头疼的事.可能一处小小的内存泄露,都可能是毁千里之堤的蚁穴. 怎么才能检测内存泄露呢? AndroidStudio 中Memory控件台(显示器)提供了一个内存监视器.我们可以通过它方便地查看应用程序的性能和内存使用情况,从而也就可以找到需要释放对象,查找内存泄漏等. 熟悉Memory界面 打开日志控制台,有一个标签Memory ,我们可以在这个界面分析当前程序使用的内存情况. 运行要监控的程序(APP)后,打开Android Monitor控制台窗口,可以看到

  • Android中LeakCanary检测内存泄漏的方法

    最近要对产品进行内存泄漏的检查,最后选择了使用Square公司开源的一个检测内存泄漏的函数库LeakCanary,在github上面搜索了一下竟然有1.6w个star,并且Android大神JakeWharton也是这个开源库的贡献者.那么就赶快拿来用吧. 先说一下我遇到的坑,我当时是直接google的,然后就直接搜索到稀土掘金的一篇关于LeakCanary的介绍,我就按照他们的文章一步步的操作,到最后才发现,他们那个build.gradle中导入的库太老了,会报这样的错误Closed Fail

  • Android性能优化之利用强大的LeakCanary检测内存泄漏及解决办法

    前言: 最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来.所以决定抽空学习总结一下这方面的知识,以及分享一下我们是如何检测内存泄漏的.我们公司使用开源框架LeakCanary来检测内存泄漏. 什么是内存泄漏? 有些对象只有有限的生命周期.当它们的任务完成之后,它们将被垃圾回收.如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,这就会导致内存泄漏

  • Android Native 内存泄漏系统化解决方案

    导读:C++内存泄漏问题的分析.定位一直是Android平台上困扰开发人员的难题.因为地图渲染.导航等核心功能对性能要求很高,高德地图APP中存在大量的C++代码.解决这个问题对于产品质量尤为重要和关键,高德地图技术团队在实践中形成了一套自己的解决方案. 分析和定位内存泄漏问题的核心在于分配函数的统计和栈回溯.如果只知道内存分配点不知道调用栈会使问题变得格外复杂,增加解决成本,因此两者缺一不可. Android中Bionic的malloc_debug模块对内存分配函数的监控及统计是比较完善的,但

  • Android内存泄漏的轻松解决方法

    前言 内存管理的目的就是让我们在开发过程中有效避免我们的应用程序出现内存泄露的问题.内存泄露相信大家都不陌生,我们可以这样理解:「没有用的对象无法回收的现象就是内存泄露」. 如果程序发生了内存泄露,则会带来以下这些问题 应用可用的内存减少,增加了堆内存的压力 降低了应用的性能,比如会触发更频繁的 GC 严重的时候可能会导致内存溢出错误,即 OOM Error 下面我们从基础说起 基础知识 Java 的内存分配简述 方法区(non-heap):编译时就分配好,在程序整个运行期间都存在.它主要存放静

  • Android Handler内存泄漏详解及其解决方案

    关联篇:深入Android的消息机制源码详解-Handler,MessageQueue与Looper关系 关联篇:HandlerThread 使用及其源码完全解析 在android开发过程中,我们可能会遇到过令人奔溃的OOM异常,面对这样的异常我们是既熟悉又深恶痛绝的,因为造成OOM的原因有很多种情况,如加载图片过大,某已不再使用的类未被GC及时回收等等......本篇我们就来分析其中一种造成OOM的场景,它就是罪恶的内存泄漏.对于这样的称呼,我们并不陌生,甚至屡次与之"并肩作战",

  • Android内存泄漏排查利器LeakCanary

    本文为大家分享了Android内存泄漏排查利器,供大家参考,具体内容如下 开源地址:https://github.com/square/leakcanary 在 build.gralde 里加上依赖, 然后sync 一下, 添加内容如下 dependencies { .... debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanar

  • Android内存溢出及内存泄漏原因进行

    内存溢出(Out Of Memory):Android系统中每一个应用程序可以向系统申请一定的内存,当申请的内存不够用的时候,就产生了内存溢出. 内存泄漏:当某个对象不再被使用,即不再有变量引用它时,该对象占用的内存就会被系统回收.当某个对象不再被使用,但是在其他对象中仍然有变量引用它时,该对象占用的内存就无法被系统回收,从而导致了内存泄漏. 当内存泄漏过多时,可用内存空间会减少,应用程序申请的内存不够用,就会导致内存溢出. 内存溢出原因: 1.内存泄漏过多. 2.内存中加载的数据量超过内存的可

  • Android LeakCanary检测内存泄露原理

    以LeakCanary2.6源码分析LeakCanary检测内存泄露原理,为减少篇幅长度,突出关键点,不粘贴大量源码,阅读时需搭配源码食用. 如何获取context LeakCanary只需引入依赖,不需要初始化代码,就能执行内存泄漏检测了,它是通过ContentProvider获取应用的context.这种获取context方式在开源第三方库中十分流行.如下AppWatcherInstaller在LeakCanary的aar包中manifest文件中注册. internal sealed cl

  • 详解LeakCanary分析内存泄露如何实现

    目录 前言 LeakCanary的使用 LeakCanary原理 源码浅析 初始化 使用 总结 前言 平时我们都有用到LeakCanary来分析内存泄露的情况,这里可以来看看LeakCanary是如何实现的,它的内部又有哪些比较有意思的操作. LeakCanary的使用 官方文档:square.github.io/leakcanary/… 引用方式 dependencies { // debugImplementation because LeakCanary should only run i

  • 分析Android常见的内存泄露和解决方案

    目录 一.前言 二.Android 内存泄露场景 2.1.非静态内部类的静态实例 2.2.多线程相关的匿名内部类/非静态内部类 2.3.Handler 内存泄露 2.4.静态 Activity 或 View 2.5.Eventbus 等注册监听造成的内存泄露 2.6.单例引起的内存泄露 2.7.资源对象没关闭造成内存泄漏 2.8.WebView 一.前言 目前 java 垃圾回收主流算法是虚拟机采用 GC Roots Tracing 算法.算法的基本思路是:通过一系列的名为 GC Roots (

  • Android App调试内存泄露之Cursor篇

    最近在工作中处理了一些内存泄露的问题,在这个过程中我尤其发现了一些基本的问题反而忽略导致内存泄露,比如静态变量,cursor关闭,线程,定时器,反注册,bitmap等等,我稍微统计并总结了一下,当然了,这些问题这么说起来比较笼统,接下来我会根据问题,把一些实例代码贴出来,一步一步分析,在具体的场景下,用行之有效的方法,找出泄露的根本原因,并给出解决方案. 现在,就从cursor关闭的问题开始把,谁都知道cursor要关闭,但是往往相反,人们却常常忘记关闭,因为真正的应用场景可能并非理想化的简单.

  • android的GC内存泄露问题

    1. android内存泄露概念 不少人认为JAVA程序,因为有垃圾回收机制,应该没有内存泄露.其实如果我们一个程序中,已经不再使用某个对象,但是因为仍然有引用指向它,垃圾回收器就无法回收它,当然该对象占用的内存就无法被使用,这就造成了内存泄露.如果我们的java运行很久,而这种内存泄露不断的发生,最后就没内存可用了.当然java的,内存泄漏和C/C++是不一样的.如果java程序完全结束后,它所有的对象就都不可达了,系统就可以对他们进行垃圾回收,它的内存泄露仅仅限于它本身,而不会影响整个系统的

  • java内存管理关系及内存泄露的原理分析

    目录 java内存管理关系及内存泄露原理 java对象和内存的关系 创建对象 null的作用 内存泄露 检测内存泄露的原理 java内存管理关系及内存泄露原理 这可能是最近写的博客中最接近底层的了.闲言少叙,进入正题. java对象和内存的关系 首先,我们要知道下面几条真理(自己总结的) 一个完整的建立对象流程是 1声明对象,2开辟内存空间,3将对象和内存空间建立联系. 一个对象只能对应一个内存空间,一个内存空间可以对应很多对象 回收一个内存空间 .如果,这个内存空间没有任何一个对象和他有联系.

  • C语言中的内存泄露 怎样避免与检测

    有些程序并不需要管理它们的动态内存的使用.当需要内存时,它们简单地通过分配来获得,从来不用担心如何释放它.这类程序包括编译器和其他一些运行一段固定的(或有限的)时间然后终止的程序.当这种类型的程序终止时,所有内存会被自动回收.细心查验每块内存是否需要回收纯属浪费时间,因为它们不会再被使用. 其他程序的生存时间要长一点.有些工具如日历管理器.邮件工具以及操作系统本事经常需要数日及至数周连续运行,并需要管理动态内存的分配和回收.由于C语言通常并不使用垃圾回收器(自动确认并回收不再使用的内存块),这些

随机推荐