欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android LeakCanary检测内存泄露原理

程序员文章站 2022-06-25 09:32:45
以leakcanary2.6源码分析leakcanary检测内存泄露原理,为减少篇幅长度,突出关键点,不粘贴大量源码,阅读时需搭配源码食用。如何获取contextleakcanary只需引入依赖,不需...

以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检测内存泄露的资料请关注其它相关文章!