溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android LeakCanary檢測(cè)內(nèi)存泄露原理是什么

發(fā)布時(shí)間:2021-03-26 13:50:38 來(lái)源:億速云 閱讀:603 作者:小新 欄目:開(kāi)發(fā)技術(shù)

小編給大家分享一下Android LeakCanary檢測(cè)內(nèi)存泄露原理是什么,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

以LeakCanary2.6源碼分析LeakCanary檢測(cè)內(nèi)存泄露原理,為減少篇幅長(zhǎng)度,突出關(guān)鍵點(diǎn),不粘貼大量源碼,閱讀時(shí)需搭配源碼食用。

如何獲取context

LeakCanary只需引入依賴,不需要初始化代碼,就能執(zhí)行內(nèi)存泄漏檢測(cè)了,它是通過(guò)ContentProvider獲取應(yīng)用的context。這種獲取context方式在開(kāi)源第三方庫(kù)中十分流行。如下AppWatcherInstaller在LeakCanary的aar包中manifest文件中注冊(cè)。

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

默認(rèn)檢測(cè)哪些類對(duì)象的內(nèi)存泄露

(1)處的方法將調(diào)用如下方法注冊(cè)需要檢測(cè)泄露的對(duì)象:

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

可以看到LeakCanary會(huì)把Activity,F(xiàn)ragment,ViewModel,RootView和Service納入檢測(cè),這些對(duì)象都是有明確的生命周期,而且占用內(nèi)存較高,它們的內(nèi)存泄露是需要我們重點(diǎn)關(guān)注的。

如何將這些生命周期對(duì)象納入監(jiān)測(cè)

(1)處的manualInstall方法將遍歷調(diào)用上述Watcher的install方法以適時(shí)將這些生命周期對(duì)象納入檢測(cè)。

ActivityWatcher

ActivityWatcher中install方法通過(guò)向application注冊(cè)Application.ActivityLifecycleCallbacks接口回調(diào)實(shí)現(xiàn)對(duì)Activity生命周期的檢測(cè)。這里有一個(gè)很棒的技巧,利用Kotlin委托與Java動(dòng)態(tài)代理,將不需要關(guān)注的方法給出默認(rèn)空實(shí)現(xiàn),(2)(3)處代碼提取出來(lái),可以在平時(shí)開(kāi)發(fā)中有需求的地方使用。

//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)調(diào)用的objectWatcher.expectWeaklyReachable方法是將對(duì)象納入監(jiān)測(cè)的通用方法,如其名稱所示,WeaklyReachable相較的是StronglyReachable,當(dāng)一個(gè)對(duì)象不再需要時(shí),我們希望它從WeaklyReachable變?yōu)镾tronglyReachable。

我們可以在不再需要某對(duì)象時(shí)主動(dòng)調(diào)用該方法,檢測(cè)任意對(duì)象(除上節(jié)的默認(rèn)對(duì)象)的內(nèi)存泄露:

AppWatcher.objectWatcher.expectWeaklyReachable(obj, "")

onActivityDestroyed回調(diào)中就通過(guò)該方式將activity納入監(jiān)測(cè)。

通過(guò)上述對(duì)Activity的納入內(nèi)存泄露源碼的分析,可以發(fā)現(xiàn)其中2個(gè)關(guān)鍵點(diǎn),首先需要能獲取應(yīng)用中所有待檢測(cè)對(duì)象的引用,其次需要一個(gè)待檢測(cè)對(duì)象生命周期結(jié)束的時(shí)機(jī)。而這兩點(diǎn)通過(guò)注冊(cè)Application.ActivityLifecycleCallbacks接口能夠同時(shí)滿足,可對(duì)于其他類對(duì)象,就沒(méi)有如此便捷的方式了。

下面介紹Fragment,ViewModel,RootView和Service這些類對(duì)象是如何納入檢測(cè)的。

FragmentAndViewModelWatcher

Fragment為了兼容在Android源碼中幾個(gè)不同包名的實(shí)現(xiàn),對(duì)它們的檢測(cè)也需要分別實(shí)現(xiàn),我們?cè)贔ragmentAndViewModelWatcher中只關(guān)注AndroidXFragmentDestroyWatcher對(duì)AndroidX中Fragment的內(nèi)存泄露檢測(cè)即可,其他幾個(gè)實(shí)現(xiàn)類似。

FragmentAndViewModelWatcher先同樣通過(guò)注冊(cè)Application.ActivityLifecycleCallbacks回調(diào),適時(shí)獲取Activity引用,并在AndroidXFragmentDestroyWatcher獲取Activity的supportFragmentManager,向其注冊(cè)FragmentManager.FragmentLifecycleCallbacks。在其中的onFragmentDestroyed與onFragmentViewDestroyed回調(diào)中將Fragment和Fragment的View納入內(nèi)存泄露檢測(cè)。

對(duì)于ViewModel的檢測(cè),則需要關(guān)注ViewModelClearedWatcher,通過(guò)用上一步獲取的Activity引用,添加名為ViewModelClearedWatcher的spy ViewModel,來(lái)獲得收到onCleared回調(diào)的能力,因?yàn)閷?duì)于一個(gè)ViewModelStoreOwner(Activity,F(xiàn)ragment)來(lái)說(shuō),自己的一個(gè)ViewModel回調(diào)了onCleared,則其他ViewModel的onCleared也應(yīng)該被調(diào)用。這些ViewModel是通過(guò)ViewModelStore的mMap屬性反射獲取的。在spy ViewModel的onCleared回調(diào)中,納入內(nèi)存泄露檢測(cè)。

RootViewWatcher

對(duì)于Android里Window中的RootView,即DecorView,可以通過(guò)注冊(cè)addOnAttachStateChangeListener在View的onViewDetachedFromWindow時(shí)進(jìn)行檢測(cè)。而獲取待檢測(cè)對(duì)象的引用就不像Activity和Fragment一樣有回調(diào)可以依賴了。LeakCanary采取了Hook的方式在install方法對(duì)RootView的容器進(jìn)行替換,具體來(lái)說(shuō)就是通過(guò)反射機(jī)制將WindowManagerGlobal中的mViews(包含所有Window中的DecorView)的ArrayList容器的實(shí)現(xiàn)修改,在其add方法中獲取DecorView的引用,之后設(shè)置OnAttachStateChangeListener回調(diào)進(jìn)行檢測(cè)。

ServiceWatcher

而Android中Service,無(wú)論是獲取引用還是監(jiān)測(cè)時(shí)機(jī)的確定都沒(méi)有系統(tǒng)的回調(diào)可以依賴,LeakCanary都是采用Hook的方式達(dá)到目的。首先通過(guò)反射拿到ActivityThread中的mServices,這是包含app中全部Service的一個(gè)Map。在install方法中有兩個(gè)Hook點(diǎn),首先是Android 消息機(jī)制的中轉(zhuǎn)中心,名為H的Handler,系統(tǒng)側(cè)對(duì)應(yīng)用側(cè)的全部回調(diào)都需要經(jīng)過(guò)它的周轉(zhuǎn)。因?yàn)镠andler中mCallback執(zhí)行的優(yōu)先級(jí)大于handleMessage方法,Leakcanary替換H的mCallback實(shí)現(xiàn),當(dāng)消息為STOP_SERVICE時(shí),便從mServices取出該消息對(duì)應(yīng)的Service作為待檢測(cè)Service引用。第二個(gè)Hook點(diǎn)為ActivityManagerService,通過(guò)動(dòng)態(tài)代理修改它的serviceDoneExecuting方法,在其真正實(shí)現(xiàn)前增加內(nèi)存泄露檢測(cè),其余方法保持不變。

這些類納入檢測(cè)納入檢測(cè)的時(shí)機(jī),可總結(jié)為如下表格:


如何獲取引用何時(shí)納入監(jiān)測(cè)
ActivityActivityLifecycleCallbacks回調(diào)onActivityDestroyed
FragmentFragmentLifecycleCallbacks回調(diào)onFragmentDestroyed
Fragment中的ViewFragmentLifecycleCallbacks回調(diào)onFragmentViewDestroyed
ViewModel反射獲取ViewModelStore的mMapspy ViewModel的onCleared
Window中的DecorViewHook WindowManagerGlobal中的mViewsonViewDetachedFromWindow
ServiceHook H的mCallback實(shí)現(xiàn),當(dāng)消息為STOP_SERVICE時(shí),從ActivityThread中的mServices獲取Hook ActivityManagerService,serviceDoneExecuting中檢測(cè)

如何確定內(nèi)存泄露的對(duì)象

在確定待檢測(cè)對(duì)象與時(shí)機(jī)后,查看ObjectWatcher的expectWeaklyReachable方法,可以得知如何實(shí)現(xiàn)將泄露對(duì)象從待檢測(cè)對(duì)象(默認(rèn)即上節(jié)我們分析的那些有生命周期的類對(duì)象)挑出來(lái)的。確定內(nèi)存泄露對(duì)象的原理是我們常用的WeakReference,其雙參數(shù)構(gòu)造函數(shù)支持傳入一個(gè)ReferenceQueue,當(dāng)其關(guān)聯(lián)的對(duì)象回收時(shí),會(huì)將WeakReference加入ReferenceQueue中。LeakCanary的做法是繼承ReferenceQueue,增加一個(gè)值為UUID的屬性key,同時(shí)將每個(gè)需要監(jiān)測(cè)的對(duì)象WeakReference以此UUID作為鍵加入一個(gè)map中。這樣,在GC過(guò)后,removeWeaklyReachableObjects方法通過(guò)遍歷ReferenceQueue,通過(guò)key值刪除map中已回收的對(duì)象,剩下的對(duì)象就基本可以確定發(fā)生了內(nèi)存泄露。

如何確定從GC root到泄露對(duì)象的引用鏈

在確定內(nèi)存泄露的對(duì)象后,就需要其他手段來(lái)確定泄露對(duì)象引用鏈了,這一過(guò)程開(kāi)始于checkRetainedObjects方法,跟蹤調(diào)用可以看到啟動(dòng)了前臺(tái)服務(wù)HeapAnalyzerService,這在我們使用LeakCanary時(shí)可以在通知欄看到。服務(wù)中調(diào)用了HeapAnalyzer的analyze方法進(jìn)行堆內(nèi)存分析,由Shark庫(kù)實(shí)現(xiàn)該功能,就不再進(jìn)行追蹤。

以上是“Android LeakCanary檢測(cè)內(nèi)存泄露原理是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI