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检测内存泄露的资料请关注其它相关文章!
上一篇: 随便就找个人过一辈子
下一篇: Android 常见bug汇总及解决方案