內(nèi)存泄漏檢測(cè)工具LeakCanary源碼解析
前言
LeakCanary
是一個(gè)簡(jiǎn)單方便的內(nèi)存泄漏檢測(cè)工具,它是由大名鼎鼎的Square公司出品并開(kāi)源的出來(lái)的。目前大部分APP在開(kāi)發(fā)階段都會(huì)接入此工具用來(lái)檢測(cè)內(nèi)存泄漏問(wèn)題。它讓我們開(kāi)發(fā)者可以在開(kāi)發(fā)階段就發(fā)現(xiàn)一些沒(méi)有注意到或者不規(guī)范的代碼導(dǎo)致的內(nèi)存泄漏,從而就避免了因內(nèi)存泄漏而最終導(dǎo)致OOM的問(wèn)題。
使用
LeakCanary
的使用非常簡(jiǎn)單,我們只需要在項(xiàng)目module下的build.gradle文件里dependencies里面加上依賴(lài)就行。
dependencies { // debugImplementation because LeakCanary should only run in debug builds. debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' }
只需要加上依賴(lài)就行了,它會(huì)在應(yīng)用啟動(dòng)的時(shí)候自動(dòng)進(jìn)行初始化。并且由于我們使用的是debugImplementation
,因此它只在debug
包下有效,所以不用擔(dān)心這個(gè)會(huì)在release
包下造成性能影響。
源碼解析
接下來(lái)將通過(guò)閱讀源碼,并從以下幾個(gè)方面去深入Leakcanary原理,本文將以2.9.1源碼為例。
- LeakCanary自動(dòng)初始化
- LeakCanary初始化做了什么
- LeakCanary默認(rèn)的4種監(jiān)聽(tīng)器
- Leakcanary對(duì)象泄漏檢查
LeakCanary自動(dòng)初始化
我們集成的時(shí)候只需要添加依賴(lài),不需要像其他第三方SDK那樣在Application或其他地方手動(dòng)調(diào)用初始化方法,其實(shí)它是借助ContentProvider
來(lái)實(shí)現(xiàn)的,通過(guò)源碼來(lái)看一下MainProcessAppWatcherInstaller
這個(gè)類(lèi):
internal class MainProcessAppWatcherInstaller : ContentProvider() { override fun onCreate(): Boolean { val application = context!!.applicationContext as Application AppWatcher.manualInstall(application) return true } ... }
如果對(duì)app的啟動(dòng)流程有了解的童鞋就清楚,APP啟動(dòng)時(shí),ContentProvider
和Application
的順序是:
Application.attachBaseContext()
-> contentProvider.onCreate()
-> Application.onCreate()
通過(guò)源碼來(lái)看下,在ActivityThread
類(lèi)里面handleBindApplication
下:
@UnsupportedAppUsage private void handleBindApplication(AppBindData data) { ... // 這邊創(chuàng)建application,并會(huì)調(diào)用到application的attach()方法,最終調(diào)用到attachBaseContext()方法 app = data.info.makeApplication(data.restrictedBackupMode, null); ... // don't bring up providers in restricted mode; they may depend on the // app's custom Application class if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { //初始化Provider并且調(diào)用了Provider的onCreate()方法 installContentProviders(app, data.providers); } } ... try { //調(diào)用了Application的onCreate()方法 mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { if (!mInstrumentation.onException(app, e)) { throw new RuntimeException( "Unable to create application " + app.getClass().getName() + ": " + e.toString(), e); } } ... }
這樣ContentProvider會(huì)在Application.onCreate()前初始化,就會(huì)調(diào)用到了LeakCanary的初始化方法,實(shí)現(xiàn)了自動(dòng)初始化。
注意: 這樣使用ContentProvider的進(jìn)行初始化的寫(xiě)法雖然方便,方便了開(kāi)發(fā)人員集成使用。但是可能會(huì)帶來(lái)啟動(dòng)耗時(shí)的問(wèn)題,并且無(wú)法控制初始化的時(shí)機(jī)。不過(guò)這對(duì)于LeakCanary這個(gè)工具庫(kù)來(lái)說(shuō)影響不大,因?yàn)檫@個(gè)也只在Debug階段使用。
如何關(guān)閉自動(dòng)初始化
如果我們想要關(guān)閉自動(dòng)化初始化,自己選擇在合適的地方進(jìn)行初始化的話,可以通過(guò)覆蓋資源文件里面的值來(lái)進(jìn)行關(guān)閉
<?xml version="1.0" encoding="utf-8"?> <resources> <bool name="leak_canary_watcher_auto_install">false</bool> </resources>
并且在合適的位置自行調(diào)用 AppWatcher.manualInstall(application)
進(jìn)行手動(dòng)初始化。
LeakCanary初始化做了什么
上面我們說(shuō)到LeakCanary是使用ContentProvider進(jìn)行初始化的,那么我們就從MainProcessAppWatcherInstaller
開(kāi)始。
internal class MainProcessAppWatcherInstaller : ContentProvider() { override fun onCreate(): Boolean { val application = context!!.applicationContext as Application //這邊調(diào)用初始化的方法 AppWatcher.manualInstall(application) return true } ... }
這邊會(huì)在這個(gè)MainProcessAppWatcherInstaller
的onCreate
方法里面調(diào)用AppWatcher.manualInstall(application)
,進(jìn)行初始化。
@JvmOverloads fun manualInstall( application: Application, retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) ) { //先判斷是否是在主線程,如果不是在主線程則拋出異常 checkMainThread() //判斷是否已經(jīng)初始化了,如果已經(jīng)初始化了則拋出異常,避免重復(fù)初始化 if (isInstalled) { throw IllegalStateException( "AppWatcher already installed, see exception cause for prior install call", installCause ) } //判斷設(shè)置的延遲時(shí)間是否小于0,這個(gè)時(shí)間是用來(lái)延遲檢測(cè)被觀察的對(duì)象是否已被釋放, //也就是說(shuō)如果到了這個(gè)時(shí)間,對(duì)象仍沒(méi)有被釋放,那么可能出現(xiàn)了內(nèi)存泄漏 check(retainedDelayMillis >= 0) { "retainedDelayMillis $retainedDelayMillis must be at least 0 ms" } this.retainedDelayMillis = retainedDelayMillis if (application.isDebuggableBuild) { LogcatSharkLog.install() } //初始化 InternalLeakCanary,并調(diào)用了InternalLeakCanary.invoke()方法, //這個(gè)類(lèi)是用來(lái)檢查判斷對(duì)象是否被回收 LeakCanaryDelegate.loadLeakCanary(application) //開(kāi)啟監(jiān)聽(tīng)器,也就是appDefaultWatchers(application)方法里面的 watchersToInstall.forEach { it.install() } // Only install after we're fully done with init. installCause = RuntimeException("manualInstall() first called here") }
在manualInstall
方法里面先做一些判斷校驗(yàn)合法,并執(zhí)行了LeakCanaryDelegate.loadLeakCanary
,這里面會(huì)調(diào)用內(nèi)部的invoke()
方法,對(duì)LeakCanary的檢查判斷泄漏的一些類(lèi)進(jìn)行初始化。接下來(lái)會(huì)對(duì)watchersToInstall
列表里面的四種觀察類(lèi)型的生命周期監(jiān)視器調(diào)用install()
方法,開(kāi)啟監(jiān)聽(tīng)。
再來(lái)看下watchersToInstall
,默認(rèn)情況下使用的是 appDefaultWatchers(application)
返回的list集合。
fun appDefaultWatchers( application: Application, reachabilityWatcher: ReachabilityWatcher = objectWatcher ): List<InstallableWatcher> { return listOf( ActivityWatcher(application, reachabilityWatcher), FragmentAndViewModelWatcher(application, reachabilityWatcher), RootViewWatcher(reachabilityWatcher), ServiceWatcher(reachabilityWatcher) ) }
這邊會(huì)創(chuàng)建ReachabilityWatcher
,也就是objectWatcher
,這邊看下他的初始化:
object AppWatcher { ... /** * The [ObjectWatcher] used by AppWatcher to detect retained objects. * Only set when [isInstalled] is true. */ val objectWatcher = ObjectWatcher( clock = { SystemClock.uptimeMillis() }, checkRetainedExecutor = { check(isInstalled) { "AppWatcher not installed" } mainHandler.postDelayed(it, retainedDelayMillis) }, isEnabled = { true } ) ... }
這邊重點(diǎn)看下checkRetainedExecutor
這個(gè)入?yún)?,它是一個(gè)Executor
,執(zhí)行run時(shí),就會(huì)調(diào)用mainHandler.postDelayed
,后面在對(duì)對(duì)象回收檢查時(shí),會(huì)調(diào)用run方法,通過(guò)postDelayed延時(shí)去檢查。
接著上面的appDefaultWatchers
方法,里面會(huì)創(chuàng)建四種類(lèi)型的生命周期監(jiān)聽(tīng)器,分別是Activity、Fragment、RootView和Service。下面將對(duì)這4者的源碼進(jìn)行分析
ActivityWatcher
class ActivityWatcher( private val application: Application, private val reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher { //回調(diào),這個(gè)是向application.registerActivityLifecycleCallbacks注冊(cè)activity生命周期回調(diào) //這邊通過(guò)監(jiān)聽(tīng)Activity的onDestroyed來(lái)觀察它的回收狀態(tài) private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityDestroyed(activity: Activity) { //一旦activity進(jìn)入到destory,則開(kāi)始通過(guò)onActivityDestroyed觀察它的回收狀態(tài) reachabilityWatcher.expectWeaklyReachable( activity, "${activity::class.java.name} received Activity#onDestroy() callback" ) } } override fun install() { application.registerActivityLifecycleCallbacks(lifecycleCallbacks) } override fun uninstall() { application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks) } }
Activity的監(jiān)聽(tīng)比較簡(jiǎn)單,只需要注冊(cè)一個(gè)回調(diào)即可,因?yàn)锳ndroid本身有提供全局監(jiān)聽(tīng)Activity的生命周期的回調(diào)。只需要在onActivityDestroyed
回調(diào)里面對(duì)對(duì)象回收情況進(jìn)行觀察,因?yàn)橐坏┻M(jìn)入到onActivityDestroyed
就表明Activity退出了,此時(shí)可以開(kāi)始觀察Activity是否回收。
這邊有一個(gè)知識(shí)點(diǎn),就是上面的Application.ActivityLifecycleCallbacks by noOpDelegate()
,這邊使用到了委托noOpDelegate()
,這個(gè)的作用是使得接口類(lèi)可以只實(shí)現(xiàn)自己想要的方法,而不需要全部實(shí)現(xiàn)。
FragmentAndViewModelWatcher
Fragment的監(jiān)聽(tīng)就比較麻煩了,需要對(duì)不同包的Fragment做適配處理,分別是:
android.app.Fragment
android.support.v4.app.Fragment
androidx.fragment.app.Fragment
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run { val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>() //添加android.app.Fragment監(jiān)聽(tīng),創(chuàng)建AndroidOFragmentDestroyWatcher,并加入到集合中 if (SDK_INT >= O) { fragmentDestroyWatchers.add( AndroidOFragmentDestroyWatcher(reachabilityWatcher) ) } //添加 androidx.fragment.app.Fragment監(jiān)聽(tīng), //通過(guò)反射的方式創(chuàng)建AndroidXFragmentDestroyWatcher,并加入到集合中 getWatcherIfAvailable( ANDROIDX_FRAGMENT_CLASS_NAME, ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, reachabilityWatcher )?.let { fragmentDestroyWatchers.add(it) } //添加android.support.v4.app.Fragment監(jiān)聽(tīng) //通過(guò)反射的方式 創(chuàng)建 AndroidSupportFragmentDestroyWatcher,并加入到集合中 getWatcherIfAvailable( ANDROID_SUPPORT_FRAGMENT_CLASS_NAME, ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, reachabilityWatcher )?.let { fragmentDestroyWatchers.add(it) } fragmentDestroyWatchers }
這邊會(huì)對(duì)不同包下的Fragment創(chuàng)建Watcher,并把它們加入到一個(gè)List中,接下來(lái)就是當(dāng)外部調(diào)用install()
時(shí),就會(huì)調(diào)用到application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
,下面來(lái)看下這個(gè)lifecycleCallbacks
private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityCreated( activity: Activity, savedInstanceState: Bundle? ) { for (watcher in fragmentDestroyWatchers) { watcher(activity) } } }
可以看到,這邊是在onActivityCreated
的時(shí)候調(diào)用各個(gè)Watcher的invoke()
方法。然后在invoke()
方法里面通過(guò)activity.fragmentManager
或activity.supportFragmentManager
調(diào)用registerFragmentLifecycleCallbacks
去注冊(cè)Fragment的生命周期回調(diào)。
這邊就只看下AndroidX下的Fragment生命周期監(jiān)聽(tīng)
internal class AndroidXFragmentDestroyWatcher( private val reachabilityWatcher: ReachabilityWatcher ) : (Activity) -> Unit { private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() { override fun onFragmentCreated( fm: FragmentManager, fragment: Fragment, savedInstanceState: Bundle? ) { ViewModelClearedWatcher.install(fragment, reachabilityWatcher) } override fun onFragmentViewDestroyed( fm: FragmentManager, fragment: Fragment ) { val view = fragment.view if (view != null) { reachabilityWatcher.expectWeaklyReachable( view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " + "(references to its views should be cleared to prevent leaks)" ) } } override fun onFragmentDestroyed( fm: FragmentManager, fragment: Fragment ) { reachabilityWatcher.expectWeaklyReachable( fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback" ) } } override fun invoke(activity: Activity) { if (activity is FragmentActivity) { val supportFragmentManager = activity.supportFragmentManager supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true) ViewModelClearedWatcher.install(activity, reachabilityWatcher) } } }
在AndroidOFragmentDestroyWatcher
類(lèi)里面,會(huì)創(chuàng)建一個(gè)Fragment的生命周期回調(diào),也就是FragmentManager.FragmentLifecycleCallbacks
,并在onFragmentViewDestroyed
和onFragmentDestroyed
這兩個(gè)回調(diào)方法里面進(jìn)行對(duì)象回收檢查。
在該類(lèi)里面,我們還能看到install了ViewModel,對(duì)ViewModel的生命周期進(jìn)行觀察。這邊會(huì)區(qū)分是Activity的ViewModel還是Fragment的ViewModel。
- Activity:在invoke()方法里面調(diào)用
ViewModelClearedWatcher.install
,并傳入Activity的ViewModelStoreOwner
。 - Fragment:在
onFragmentCreated
回調(diào)里面調(diào)用了ViewModelClearedWatcher.install
,傳入Fragment的ViewModelStoreOwner
。
那么在ViewModelClearedWatcher
里面,又是怎么對(duì)viewModel的生命周期進(jìn)行觀察的呢?這邊就不貼代碼了:
- 在install方法里面創(chuàng)建一個(gè)ViewModel,也就是
ViewModelClearedWatcher
,加入到對(duì)應(yīng)傳入的ViewModelStoreOwner
下的ViewModelStore
里的集合里面。 - 然后在
ViewModelClearedWatcher
的onCleared()
方法里面通過(guò)反射的方式取出ViewModelStore
里面的集合,進(jìn)行迭代遍歷,對(duì)集合里面的所有ViewModel開(kāi)啟對(duì)象回收檢查。
RootViewWatcher
private val listener = OnRootViewAddedListener { rootView -> val trackDetached = when(rootView.windowType) { PHONE_WINDOW -> { when (rootView.phoneWindow?.callback?.wrappedCallback) { // Activities are already tracked by ActivityWatcher // activity不需要重復(fù)注冊(cè) is Activity -> false //監(jiān)聽(tīng)dialog,這邊還需要判斷下資源文件里面的配置信息是否是打開(kāi)的 is Dialog -> { // Use app context resources to avoid NotFoundException // https://github.com/square/leakcanary/issues/2137 val resources = rootView.context.applicationContext.resources resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs) } // Probably a DreamService // 其他情況都打開(kāi) else -> true } } // Android widgets keep detached popup window instances around. POPUP_WINDOW -> false TOOLTIP, TOAST, UNKNOWN -> true } if (trackDetached) { rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener { val watchDetachedView = Runnable { reachabilityWatcher.expectWeaklyReachable( rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback" ) } override fun onViewAttachedToWindow(v: View) { //當(dāng)顯示時(shí)移除內(nèi)存監(jiān)聽(tīng)器 mainHandler.removeCallbacks(watchDetachedView) } override fun onViewDetachedFromWindow(v: View) { //當(dāng)view從界面上移除時(shí)時(shí),就執(zhí)行監(jiān)聽(tīng),通過(guò)Handler.post() mainHandler.post(watchDetachedView) } }) } } override fun install() { //使用三方庫(kù)的Curtains來(lái)對(duì)view進(jìn)行狀態(tài)的監(jiān)聽(tīng) Curtains.onRootViewsChangedListeners += listener }
RootView這邊只要監(jiān)聽(tīng)一些dialog、tooltip、toast等,代碼不長(zhǎng),簡(jiǎn)單說(shuō)下
- 創(chuàng)建一個(gè)
OnRootViewAddedListener
監(jiān)聽(tīng),使用的是第三方庫(kù)Curtains,在回調(diào)里面拿到rootView; - 對(duì)這個(gè)rootView注冊(cè)一個(gè)
OnAttachStateChangeListener
監(jiān)聽(tīng),在onViewAttachedToWindow
方法里面移除對(duì)象回收檢查。在onViewDetachedFromWindow
里面開(kāi)啟對(duì)象回收檢查;
ServiceWatcher
//存放即將stop的service private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>() private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") } //通過(guò)反射的方式調(diào)用ActivityThread里面的currentActivityThread方法,也就是取到ActivityThread對(duì)象 private val activityThreadInstance by lazy { activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!! } //通過(guò)反射方式從ActivityThread取出mServices private val activityThreadServices by lazy { val mServicesField = activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true } @Suppress("UNCHECKED_CAST") mServicesField[activityThreadInstance] as Map<IBinder, Service> } override fun install() { //做一些檢查 checkMainThread() check(uninstallActivityThreadHandlerCallback == null) { "ServiceWatcher already installed" } check(uninstallActivityManager == null) { "ServiceWatcher already installed" } try { //hook ActivityThread里面的mH,也就是Handler,并替換里面的mCallBack swapActivityThreadHandlerCallback { mCallback -> //還原h(huán)ook的mCallback,uninstall()時(shí)使用 uninstallActivityThreadHandlerCallback = { swapActivityThreadHandlerCallback { mCallback } } //返回需要替換的mCallback,在這里面監(jiān)聽(tīng)handler消息, Handler.Callback { msg -> // https://github.com/square/leakcanary/issues/2114 // On some Motorola devices (Moto E5 and G6), the msg.obj returns an ActivityClientRecord // instead of an IBinder. This crashes on a ClassCastException. Adding a type check // here to prevent the crash. if (msg.obj !is IBinder) { return@Callback false } //如果收到了Service的Stop的消息,表示service要結(jié)束了, //此時(shí)調(diào)用onServicePreDestroy,將當(dāng)前service加入到servicesToBeDestroyed集合里面去 if (msg.what == STOP_SERVICE) { val key = msg.obj as IBinder activityThreadServices[key]?.let { onServicePreDestroy(key, it) } } //繼續(xù)處理消息 mCallback?.handleMessage(msg) ?: false } } //這邊hook ActivityManagerService swapActivityManager { activityManagerInterface, activityManagerInstance -> uninstallActivityManager = { swapActivityManager { _, _ -> activityManagerInstance } } //使用動(dòng)態(tài)代理的方式 Proxy.newProxyInstance( activityManagerInterface.classLoader, arrayOf(activityManagerInterface) ) { _, method, args -> //如果執(zhí)行serviceDoneExecuting方法時(shí),則調(diào)用onServiceDestroyed() //方法來(lái)觀察service的內(nèi)存回收情況 if (METHOD_SERVICE_DONE_EXECUTING == method.name) { val token = args!![0] as IBinder if (servicesToBeDestroyed.containsKey(token)) { onServiceDestroyed(token) } } try { if (args == null) { method.invoke(activityManagerInstance) } else { method.invoke(activityManagerInstance, *args) } } catch (invocationException: InvocationTargetException) { throw invocationException.targetException } } } } catch (ignored: Throwable) { SharkLog.d(ignored) { "Could not watch destroyed services" } } }
Service的結(jié)束監(jiān)聽(tīng)是通過(guò)hool的方式進(jìn)行的,步驟如下:
- 通過(guò)反射的方取出
ActivityThread
里面的mH
,也就是Handler
。然后再取出該Handler
里面的mCallback
,并通過(guò)反射,使用自己創(chuàng)建的callBack去替換,然后在Callback里面監(jiān)聽(tīng)Handle消息,如果收到的消息是msg.what == STOP_SERVICE
,則表示Service即將結(jié)束,此時(shí)將該service加入到待觀察集合里面去。 - 接下來(lái)通過(guò)hook的方式,hook住
ActivityManagerService
。使用動(dòng)態(tài)代理,如果執(zhí)行了serviceDoneExecuting
方法,則表示service結(jié)束,此時(shí)從待觀察集合里面取出當(dāng)前這個(gè)service并從待觀察列表里面移除,然后觀察這個(gè)service對(duì)象的回收情況。
Leakcanary對(duì)象泄漏檢查
在被觀察類(lèi)型的生命周期的結(jié)束時(shí),會(huì)調(diào)用到reachabilityWatcher.expectWeaklyReachable
這個(gè)方法
我們跟進(jìn)去,并找到實(shí)現(xiàn)類(lèi),來(lái)到ObjectWatcher
里面,找到expectWeaklyReachable
方法。
ObjectWatcher: @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) { //這邊在前面初始化的時(shí)候默認(rèn)傳進(jìn)來(lái)是true,因此會(huì)繼續(xù)往下執(zhí)行下面的代碼 if (!isEnabled()) { return } //這邊會(huì)先先處理下,將被回收的對(duì)象從watchedObjects這個(gè)待觀察的集合里面移除 removeWeaklyReachableObjects() //創(chuàng)建一個(gè)隨機(jī)的UUID,用來(lái)當(dāng)做待觀察對(duì)象的key,方便從watchedObjects這個(gè)map取值 val key = UUID.randomUUID() .toString() // 獲取當(dāng)前的時(shí)間,自系統(tǒng)開(kāi)機(jī)到現(xiàn)在的一個(gè)時(shí)間 val watchUptimeMillis = clock.uptimeMillis() //創(chuàng)建弱引用對(duì)象,也就是WeakReference val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) SharkLog.d { "Watching " + (if (watchedObject is Class<*>) watchedObject.toString() else "instance of ${watchedObject.javaClass.name}") + (if (description.isNotEmpty()) " ($description)" else "") + " with key $key" } //把對(duì)象放入到待觀察集合里面去 watchedObjects[key] = reference //執(zhí)行延時(shí)操作,默認(rèn)延時(shí)5秒,去檢查對(duì)象是否回收 checkRetainedExecutor.execute { moveToRetained(key) } }
在這個(gè)方法里面會(huì)先執(zhí)行一次removeWeaklyReachableObjects()
,將已被回收的對(duì)象從待觀察的集合watchedObjects
里面移除,然后創(chuàng)建弱引用對(duì)象,接著開(kāi)啟延時(shí)檢查,默認(rèn)等待5秒,使用的是mainHandler.postDelayed(it, retainedDelayMillis)
去做延時(shí)的。
先看下removeWeaklyReachableObjects()
這個(gè)方法:
private fun removeWeaklyReachableObjects() { // WeakReferences are enqueued as soon as the object to which they point to becomes weakly // reachable. This is before finalization or garbage collection has actually happened. var ref: KeyedWeakReference? //通過(guò)迭代遍歷的方式,判斷queue里面時(shí)候有被回收的對(duì)象 do { ref = queue.poll() as KeyedWeakReference? if (ref != null) { //如果對(duì)象被回收了 ,則講弱引用從待觀察的map里面移除 watchedObjects.remove(ref.key) } } while (ref != null) }
這個(gè)方法里面代碼很少,這里面做的工作就是判斷queue里面有沒(méi)有被回收的對(duì)象,如果有則將改對(duì)象的弱引用從待觀察的集合里面移除。
然后在來(lái)看下延時(shí)處理里面做了什么事:
@Synchronized private fun moveToRetained(key: String) { removeWeaklyReachableObjects() val retainedRef = watchedObjects[key] if (retainedRef != null) { retainedRef.retainedUptimeMillis = clock.uptimeMillis() onObjectRetainedListeners.forEach { it.onObjectRetained() } } }
可以看到里面一開(kāi)始又調(diào)用了一次removeWeaklyReachableObjects()
方法。這邊是第二次調(diào)用了,嘗試移除這5秒內(nèi)已經(jīng)被回收的對(duì)象。如果此時(shí)對(duì)象仍沒(méi)有被回收,也就是還在待觀察集合里面,那么就開(kāi)始進(jìn)入到回調(diào)里面去,也就是OnObjectRetainedListener.onObjectRetained()
里面去,這邊這個(gè)onObjectRetainedListeners
列表里面目前就只有一個(gè), 它是在InternalLeakCanary
的invoke
里面調(diào)用的。
AppWatcher -> manualIstall() -> LeakCanaryDelegate.loadLeakCanary(application) -> InternalLeakCanary -> invoke()
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener { ... override fun invoke(application: Application) { _application = application checkRunningInDebuggableBuild() //添加監(jiān)聽(tīng),因?yàn)镮nternalLeakCanary實(shí)現(xiàn)了OnObjectRetainedListener接口,因此直接傳this AppWatcher.objectWatcher.addOnObjectRetainedListener(this) ... } ... }
因此最終是回調(diào)到InternalLeakCanary
的onObjectRetained()
方法里面,然后在調(diào)用到scheduleRetainedObjectCheck()
。最終進(jìn)入到heapDumpTrigger.scheduleRetainedObjectCheck()
方法里面
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener { ... //回調(diào)到這個(gè)方法,然后又調(diào)用了scheduleRetainedObjectCheck override fun onObjectRetained() = scheduleRetainedObjectCheck() fun scheduleRetainedObjectCheck() { //先判斷heapDumpTrigger是否有初始化了, if (this::heapDumpTrigger.isInitialized) { heapDumpTrigger.scheduleRetainedObjectCheck() } } ... }
接下來(lái)在進(jìn)入到heapDumpTrigger.scheduleRetainedObjectCheck()
方法里面
fun scheduleRetainedObjectCheck( delayMillis: Long = 0L ) { val checkCurrentlyScheduledAt = checkScheduledAt //如果之前的子線程任務(wù)來(lái)沒(méi)開(kāi)始執(zhí)行則返回,也就是就是下面的backgroundHandler.postDelayed if (checkCurrentlyScheduledAt > 0) { return } checkScheduledAt = SystemClock.uptimeMillis() + delayMillis //讓子線程延時(shí)去delayMillis執(zhí)行,通過(guò)上面的調(diào)用鏈進(jìn)入此方法則delayMillis為0,因此會(huì)馬上執(zhí)行 backgroundHandler.postDelayed({ //子線程任務(wù)開(kāi)始執(zhí)行,就將checkScheduledAt設(shè)置為0,以便下一次出現(xiàn)內(nèi)存泄漏時(shí)能還能進(jìn)來(lái) checkScheduledAt = 0 checkRetainedObjects() }, delayMillis) }
接下來(lái)在進(jìn)入到checkRetainedObjects()
這個(gè)方法里面去。由于上面是通過(guò)子線程Handler去post,因此該方法是運(yùn)行在子線程里面
private fun checkRetainedObjects() { ... //看下此時(shí)待觀察的集合里面還有多少個(gè)沒(méi)有被分析的弱引用對(duì)象 //也就是retainedUptimeMillis != -1L 的對(duì)象的個(gè)數(shù) //并且在調(diào)用retainedObjectCount.get()的時(shí)候,還會(huì)再調(diào)用一次removeWeaklyReachableObjects(),嘗試再次移除一遍 var retainedReferenceCount = objectWatcher.retainedObjectCount //如果此時(shí)還有泄漏對(duì)象,則調(diào)用gc,并重新再獲取一次數(shù)量 if (retainedReferenceCount > 0) { gcTrigger.runGc() retainedReferenceCount = objectWatcher.retainedObjectCount } //這邊判斷應(yīng)用是出于前臺(tái)還是后臺(tái), //如果是在前臺(tái),則個(gè)數(shù)達(dá)到5個(gè)時(shí)才dump,如果應(yīng)用在后臺(tái)則只要有一個(gè)都會(huì)進(jìn)行dump if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return val now = SystemClock.uptimeMillis() val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis //如果這個(gè)時(shí)間與上次dump的時(shí)間過(guò)短,則會(huì)重新調(diào)用scheduleRetainedObjectCheck, //并延遲執(zhí)行,這個(gè)延遲的時(shí)間 = 60s - 時(shí)間差,避免兩次dump的時(shí)間過(guò)短,影響使用 if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) { onRetainInstanceListener.onEvent(DumpHappenedRecently) showRetainedCountNotification( objectCount = retainedReferenceCount, contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait) ) scheduleRetainedObjectCheck( delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis ) return } dismissRetainedCountNotification() val visibility = if (applicationVisible) "visible" else "not visible" //這邊開(kāi)始進(jìn)行dump dumpHeap( retainedReferenceCount = retainedReferenceCount, retry = true, reason = "$retainedReferenceCount retained objects, app is $visibility" ) }
在這個(gè)checkRetainedObjects()
方法里面執(zhí)行的步驟:
- 先獲取待觀察集合里面被還沒(méi)有被分析的疑似泄漏的弱引用對(duì)象的個(gè)數(shù),通過(guò)
retainedUptimeMillis != -1L
這個(gè)判斷,因?yàn)楫?dāng)對(duì)象被通過(guò)dump分析之后,會(huì)將retainedUptimeMillis這個(gè)時(shí)間改為-1。而且在objectWatcher.retainedObjectCount
這個(gè)get的方法里面還會(huì)執(zhí)行一次removeWeaklyReachableObjects()
,嘗試再次移除被回收的弱引用對(duì)象。 - 如果此時(shí)還是有泄漏對(duì)象,也就是retainedReferenceCount > 0時(shí),則會(huì)調(diào)用一次
gcTrigger.runGc()
進(jìn)行一次GC,然后在重新獲取下個(gè)數(shù)。 - 接著判斷應(yīng)用是在前臺(tái)還是后臺(tái),如果應(yīng)用此時(shí)是在前臺(tái),則只有當(dāng)個(gè)數(shù)達(dá)到5個(gè)時(shí)才進(jìn)行dump。如果是在后臺(tái),則只要有一個(gè)就會(huì)進(jìn)行dump。
- 接下來(lái)在判斷下當(dāng)前時(shí)間距離上一次dump的時(shí)間,如果時(shí)間過(guò)短(小于60S),則不進(jìn)行dump,而是重新調(diào)用
scheduleRetainedObjectCheck
并延時(shí)執(zhí)行,延遲時(shí)間為:60秒 - 時(shí)間差。并且這邊會(huì)返回,不再執(zhí)行之后的dump - 最后執(zhí)行dump,將內(nèi)存泄漏的案發(fā)現(xiàn)場(chǎng)保存下來(lái),并解析。
接下來(lái)來(lái)看下dumpHeap這個(gè)方法,這邊就不分析dump的源碼了,主要看下dump后執(zhí)行操作。
private fun dumpHeap( retainedReferenceCount: Int, retry: Boolean, reason: String ) { ... val heapDumpUptimeMillis = SystemClock.uptimeMillis() ... //執(zhí)行dump,并計(jì)算dump的時(shí)間 durationMillis = measureDurationMillis { //執(zhí)行dump,這邊就不分析了,感興趣可以自行點(diǎn)進(jìn)去看下 configProvider().heapDumper.dumpHeap(heapDumpFile) } ... //dump之后處理以分析的對(duì)象 objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis) }
這邊注意下:objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
這個(gè)方法,上面我們說(shuō)到當(dāng)對(duì)象被通過(guò)dump分析之后,會(huì)將retainedUptimeMillis這個(gè)時(shí)間改為-1?,F(xiàn)在我們來(lái)看下這個(gè)方法里面的源碼:
/** * Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on * [clock] [Clock.uptimeMillis]) */ @Synchronized fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) { //從待觀察集合里面過(guò)濾出時(shí)間小于等于當(dāng)前dump時(shí)間的弱引用 val weakRefsToRemove = watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis } //遍歷執(zhí)行clear,將時(shí)間設(shè)置為-1,這個(gè)在下次觸發(fā)執(zhí)行checkRetainedObjects,里面不會(huì)重復(fù)判斷 weakRefsToRemove.values.forEach { it.clear() } //然后從待觀察列表里面移除 watchedObjects.keys.removeAll(weakRefsToRemove.keys) }
這個(gè)方法里面會(huì)過(guò)濾出已經(jīng)被dump過(guò)的弱引用對(duì)象,因?yàn)楫?dāng)它的watchUptimeMillis <= heapDumpUptimeMillis時(shí),dump會(huì)將內(nèi)存中當(dāng)前時(shí)間點(diǎn)之前的疑似泄漏的對(duì)象都列舉出來(lái),所以只要是在當(dāng)前dump時(shí)間點(diǎn)之前加入的都可以認(rèn)為是已經(jīng)dump了。
然后接下調(diào)用過(guò)濾出來(lái)的弱引用對(duì)象的claer()
方法,將watchUptimeMillis
時(shí)間設(shè)置為-1,并且從待觀察集合里面移除。
總結(jié)
經(jīng)過(guò)了上面的源碼分析,接下來(lái)來(lái)總結(jié)下:
- 使用ContentProvider進(jìn)行自動(dòng)初始化,在ContentProvider的onCreate()方法里面調(diào)用
AppWatcher.manualInstall(application)
進(jìn)行LeakCanary的初始化; - 在
AppWatcher.manualInstall(application)
里面先做了一些合法性校驗(yàn),然后初始化初始化了InternalLeakCanary
和構(gòu)建了4種生命周期監(jiān)聽(tīng)器Watcher,分別是Activity、Fragment、Service和RootView。 - 在構(gòu)建4種Watcher時(shí),順便創(chuàng)建了
objectWatcher
,用于之后延時(shí)檢查對(duì)象是否被回收。當(dāng)被被觀察的類(lèi)型在生命周期進(jìn)入結(jié)束時(shí),會(huì)調(diào)用到objectWatcher.expectWeaklyReachable()
方法里面。 - 在
objectWatcher.expectWeaklyReachable()
方法里面會(huì)先將被回收的對(duì)象移除待觀察集合,然后創(chuàng)建一個(gè)弱引用放入到待觀察列表里面,最后在開(kāi)啟延時(shí)檢查對(duì)象是否被回收,默認(rèn)延時(shí)5秒。 - 延時(shí)計(jì)時(shí)到了之后會(huì)調(diào)用
objectWatcher.moveToRetained
方法,在該方法內(nèi)會(huì)再次嘗試移除這5秒內(nèi)已經(jīng)被回收的對(duì)象,然后調(diào)用InternalLeakCanary.onObjectRetained()
方法,跟著調(diào)用棧進(jìn)入到heapDumpTrigger.scheduleRetainedObjectCheck()
方法,最終到了checkRetainedObjects()
這個(gè)方法里面。 - 在
checkRetainedObjects()
這個(gè)方法里面,會(huì)先判斷待觀察結(jié)合里面是否還有疑似泄漏的弱引用對(duì)象,如果有則嘗試進(jìn)行一次GC操作。然后判斷應(yīng)用是在前臺(tái)還是后臺(tái),如果實(shí)在前臺(tái)則當(dāng)疑似泄漏的弱引用對(duì)象數(shù)量達(dá)到5個(gè)時(shí)才進(jìn)行dump。如果是在后臺(tái),則只要有一個(gè)就會(huì)進(jìn)行dump。 - 然后判斷當(dāng)前時(shí)間是否距離上次dump的時(shí)間小于60秒,如果小于60秒則重新進(jìn)入到
heapDumpTrigger.scheduleRetainedObjectCheck()
方法,并延時(shí)開(kāi)啟任務(wù),延時(shí)時(shí)間為:60秒 - 兩次dump的時(shí)間差。 - 最后進(jìn)行dump,和dump文件的數(shù)據(jù)分析。dump完成后,會(huì)將待觀察列表里面的已被分析過(guò)的弱引用對(duì)象的
watchUptimeMillis
時(shí)間設(shè)置為-1,并移除。
以上就是LeakCanary 2.9.1的源碼解析,更多關(guān)于內(nèi)存泄漏檢測(cè)LeakCanary的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android超詳細(xì)講解組件LinearLayout的使用
LinearLayout又稱(chēng)作線性布局,是一種非常常用的布局。正如它的名字所描述的一樣,這個(gè)布局會(huì)將它所包含的控件在線性方向上依次排列。既然是線性排列,肯定就不僅只有一個(gè)方向,這里一般只有兩個(gè)方向:水平方向和垂直方向2022-03-03關(guān)于Touch Panel AA區(qū)要做外擴(kuò)的原因解析
今天小編就為大家分享一篇關(guān)于Touch Panel AA區(qū)要做外擴(kuò)的原因解析,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12RecyclerView實(shí)現(xiàn)拖拽排序效果
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)拖拽排序效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android實(shí)用小技巧之利用Lifecycle寫(xiě)出更好維護(hù)的代碼
lifecycle是一個(gè)類(lèi),用于存儲(chǔ)有關(guān)組件(如Activity或Fragment)的生命周期狀態(tài)的信息,并允許其他對(duì)象觀察此狀態(tài),下面這篇文章主要給大家介紹了關(guān)于Android實(shí)用小技巧之利用Lifecycle寫(xiě)出更好維護(hù)的代碼的相關(guān)資料,需要的朋友可以參考下2022-05-05Android app開(kāi)發(fā)中Retrofit框架的初步上手使用
這篇文章主要介紹了Android app開(kāi)發(fā)中Retrofit框架的初步上手使用,Retrofit 2.0發(fā)布以來(lái)獲得了巨大的人氣增長(zhǎng),并且經(jīng)常被開(kāi)發(fā)者們拿來(lái)與Volley比較,需要的朋友可以參考下2016-02-02Android ListView列表控件的介紹和性能優(yōu)化
這篇文章主要介紹了Android ListView列表控件的介紹和性能優(yōu)化,需要的朋友可以參考下2017-06-06Android編程實(shí)現(xiàn)動(dòng)態(tài)支持多語(yǔ)言的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)動(dòng)態(tài)支持多語(yǔ)言的方法,涉及Android資源、控件及屬性相關(guān)操作技巧,需要的朋友可以參考下2017-06-06