內(nèi)存泄漏檢測工具LeakCanary源碼解析
前言
LeakCanary
是一個(gè)簡單方便的內(nèi)存泄漏檢測工具,它是由大名鼎鼎的Square公司出品并開源的出來的。目前大部分APP在開發(fā)階段都會(huì)接入此工具用來檢測內(nèi)存泄漏問題。它讓我們開發(fā)者可以在開發(fā)階段就發(fā)現(xiàn)一些沒有注意到或者不規(guī)范的代碼導(dǎo)致的內(nèi)存泄漏,從而就避免了因內(nèi)存泄漏而最終導(dǎo)致OOM的問題。
使用
LeakCanary
的使用非常簡單,我們只需要在項(xiàng)目module下的build.gradle文件里dependencies里面加上依賴就行。
dependencies { // debugImplementation because LeakCanary should only run in debug builds. debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' }
只需要加上依賴就行了,它會(huì)在應(yīng)用啟動(dòng)的時(shí)候自動(dòng)進(jìn)行初始化。并且由于我們使用的是debugImplementation
,因此它只在debug
包下有效,所以不用擔(dān)心這個(gè)會(huì)在release
包下造成性能影響。
源碼解析
接下來將通過閱讀源碼,并從以下幾個(gè)方面去深入Leakcanary原理,本文將以2.9.1源碼為例。
- LeakCanary自動(dòng)初始化
- LeakCanary初始化做了什么
- LeakCanary默認(rèn)的4種監(jiān)聽器
- Leakcanary對象泄漏檢查
LeakCanary自動(dòng)初始化
我們集成的時(shí)候只需要添加依賴,不需要像其他第三方SDK那樣在Application或其他地方手動(dòng)調(diào)用初始化方法,其實(shí)它是借助ContentProvider
來實(shí)現(xiàn)的,通過源碼來看一下MainProcessAppWatcherInstaller
這個(gè)類:
internal class MainProcessAppWatcherInstaller : ContentProvider() { override fun onCreate(): Boolean { val application = context!!.applicationContext as Application AppWatcher.manualInstall(application) return true } ... }
如果對app的啟動(dòng)流程有了解的童鞋就清楚,APP啟動(dòng)時(shí),ContentProvider
和Application
的順序是:
Application.attachBaseContext()
-> contentProvider.onCreate()
-> Application.onCreate()
通過源碼來看下,在ActivityThread
類里面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)行初始化的寫法雖然方便,方便了開發(fā)人員集成使用。但是可能會(huì)帶來啟動(dòng)耗時(shí)的問題,并且無法控制初始化的時(shí)機(jī)。不過這對于LeakCanary這個(gè)工具庫來說影響不大,因?yàn)檫@個(gè)也只在Debug階段使用。
如何關(guān)閉自動(dòng)初始化
如果我們想要關(guān)閉自動(dòng)化初始化,自己選擇在合適的地方進(jìn)行初始化的話,可以通過覆蓋資源文件里面的值來進(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初始化做了什么
上面我們說到LeakCanary是使用ContentProvider進(jìn)行初始化的,那么我們就從MainProcessAppWatcherInstaller
開始。
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í)間是用來延遲檢測被觀察的對象是否已被釋放, //也就是說如果到了這個(gè)時(shí)間,對象仍沒有被釋放,那么可能出現(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è)類是用來檢查判斷對象是否被回收 LeakCanaryDelegate.loadLeakCanary(application) //開啟監(jiān)聽器,也就是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()
方法,對LeakCanary的檢查判斷泄漏的一些類進(jìn)行初始化。接下來會(huì)對watchersToInstall
列表里面的四種觀察類型的生命周期監(jiān)視器調(diào)用install()
方法,開啟監(jiān)聽。
再來看下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
,后面在對對象回收檢查時(shí),會(huì)調(diào)用run方法,通過postDelayed延時(shí)去檢查。
接著上面的appDefaultWatchers
方法,里面會(huì)創(chuàng)建四種類型的生命周期監(jiān)聽器,分別是Activity、Fragment、RootView和Service。下面將對這4者的源碼進(jìn)行分析
ActivityWatcher
class ActivityWatcher( private val application: Application, private val reachabilityWatcher: ReachabilityWatcher ) : InstallableWatcher { //回調(diào),這個(gè)是向application.registerActivityLifecycleCallbacks注冊activity生命周期回調(diào) //這邊通過監(jiān)聽Activity的onDestroyed來觀察它的回收狀態(tài) private val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks by noOpDelegate() { override fun onActivityDestroyed(activity: Activity) { //一旦activity進(jìn)入到destory,則開始通過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)聽比較簡單,只需要注冊一個(gè)回調(diào)即可,因?yàn)锳ndroid本身有提供全局監(jiān)聽Activity的生命周期的回調(diào)。只需要在onActivityDestroyed
回調(diào)里面對對象回收情況進(jìn)行觀察,因?yàn)橐坏┻M(jìn)入到onActivityDestroyed
就表明Activity退出了,此時(shí)可以開始觀察Activity是否回收。
這邊有一個(gè)知識(shí)點(diǎn),就是上面的Application.ActivityLifecycleCallbacks by noOpDelegate()
,這邊使用到了委托noOpDelegate()
,這個(gè)的作用是使得接口類可以只實(shí)現(xiàn)自己想要的方法,而不需要全部實(shí)現(xiàn)。
FragmentAndViewModelWatcher
Fragment的監(jiān)聽就比較麻煩了,需要對不同包的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)聽,創(chuàng)建AndroidOFragmentDestroyWatcher,并加入到集合中 if (SDK_INT >= O) { fragmentDestroyWatchers.add( AndroidOFragmentDestroyWatcher(reachabilityWatcher) ) } //添加 androidx.fragment.app.Fragment監(jiān)聽, //通過反射的方式創(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)聽 //通過反射的方式 創(chuàng)建 AndroidSupportFragmentDestroyWatcher,并加入到集合中 getWatcherIfAvailable( ANDROID_SUPPORT_FRAGMENT_CLASS_NAME, ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME, reachabilityWatcher )?.let { fragmentDestroyWatchers.add(it) } fragmentDestroyWatchers }
這邊會(huì)對不同包下的Fragment創(chuàng)建Watcher,并把它們加入到一個(gè)List中,接下來就是當(dāng)外部調(diào)用install()
時(shí),就會(huì)調(diào)用到application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
,下面來看下這個(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()
方法里面通過activity.fragmentManager
或activity.supportFragmentManager
調(diào)用registerFragmentLifecycleCallbacks
去注冊Fragment的生命周期回調(diào)。
這邊就只看下AndroidX下的Fragment生命周期監(jiān)聽
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
類里面,會(huì)創(chuàng)建一個(gè)Fragment的生命周期回調(diào),也就是FragmentManager.FragmentLifecycleCallbacks
,并在onFragmentViewDestroyed
和onFragmentDestroyed
這兩個(gè)回調(diào)方法里面進(jìn)行對象回收檢查。
在該類里面,我們還能看到install了ViewModel,對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
里面,又是怎么對viewModel的生命周期進(jìn)行觀察的呢?這邊就不貼代碼了:
- 在install方法里面創(chuàng)建一個(gè)ViewModel,也就是
ViewModelClearedWatcher
,加入到對應(yīng)傳入的ViewModelStoreOwner
下的ViewModelStore
里的集合里面。 - 然后在
ViewModelClearedWatcher
的onCleared()
方法里面通過反射的方式取出ViewModelStore
里面的集合,進(jìn)行迭代遍歷,對集合里面的所有ViewModel開啟對象回收檢查。
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ù)注冊 is Activity -> false //監(jiān)聽dialog,這邊還需要判斷下資源文件里面的配置信息是否是打開的 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 // 其他情況都打開 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)聽器 mainHandler.removeCallbacks(watchDetachedView) } override fun onViewDetachedFromWindow(v: View) { //當(dāng)view從界面上移除時(shí)時(shí),就執(zhí)行監(jiān)聽,通過Handler.post() mainHandler.post(watchDetachedView) } }) } } override fun install() { //使用三方庫的Curtains來對view進(jìn)行狀態(tài)的監(jiān)聽 Curtains.onRootViewsChangedListeners += listener }
RootView這邊只要監(jiān)聽一些dialog、tooltip、toast等,代碼不長,簡單說下
- 創(chuàng)建一個(gè)
OnRootViewAddedListener
監(jiān)聽,使用的是第三方庫Curtains,在回調(diào)里面拿到rootView; - 對這個(gè)rootView注冊一個(gè)
OnAttachStateChangeListener
監(jiān)聽,在onViewAttachedToWindow
方法里面移除對象回收檢查。在onViewDetachedFromWindow
里面開啟對象回收檢查;
ServiceWatcher
//存放即將stop的service private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>() private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") } //通過反射的方式調(diào)用ActivityThread里面的currentActivityThread方法,也就是取到ActivityThread對象 private val activityThreadInstance by lazy { activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!! } //通過反射方式從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)聽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() //方法來觀察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)聽是通過hool的方式進(jìn)行的,步驟如下:
- 通過反射的方取出
ActivityThread
里面的mH
,也就是Handler
。然后再取出該Handler
里面的mCallback
,并通過反射,使用自己創(chuàng)建的callBack去替換,然后在Callback里面監(jiān)聽Handle消息,如果收到的消息是msg.what == STOP_SERVICE
,則表示Service即將結(jié)束,此時(shí)將該service加入到待觀察集合里面去。 - 接下來通過hook的方式,hook住
ActivityManagerService
。使用動(dòng)態(tài)代理,如果執(zhí)行了serviceDoneExecuting
方法,則表示service結(jié)束,此時(shí)從待觀察集合里面取出當(dāng)前這個(gè)service并從待觀察列表里面移除,然后觀察這個(gè)service對象的回收情況。
Leakcanary對象泄漏檢查
在被觀察類型的生命周期的結(jié)束時(shí),會(huì)調(diào)用到reachabilityWatcher.expectWeaklyReachable
這個(gè)方法
我們跟進(jìn)去,并找到實(shí)現(xiàn)類,來到ObjectWatcher
里面,找到expectWeaklyReachable
方法。
ObjectWatcher: @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) { //這邊在前面初始化的時(shí)候默認(rèn)傳進(jìn)來是true,因此會(huì)繼續(xù)往下執(zhí)行下面的代碼 if (!isEnabled()) { return } //這邊會(huì)先先處理下,將被回收的對象從watchedObjects這個(gè)待觀察的集合里面移除 removeWeaklyReachableObjects() //創(chuàng)建一個(gè)隨機(jī)的UUID,用來當(dāng)做待觀察對象的key,方便從watchedObjects這個(gè)map取值 val key = UUID.randomUUID() .toString() // 獲取當(dāng)前的時(shí)間,自系統(tǒng)開機(jī)到現(xiàn)在的一個(gè)時(shí)間 val watchUptimeMillis = clock.uptimeMillis() //創(chuàng)建弱引用對象,也就是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" } //把對象放入到待觀察集合里面去 watchedObjects[key] = reference //執(zhí)行延時(shí)操作,默認(rèn)延時(shí)5秒,去檢查對象是否回收 checkRetainedExecutor.execute { moveToRetained(key) } }
在這個(gè)方法里面會(huì)先執(zhí)行一次removeWeaklyReachableObjects()
,將已被回收的對象從待觀察的集合watchedObjects
里面移除,然后創(chuàng)建弱引用對象,接著開啟延時(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? //通過迭代遍歷的方式,判斷queue里面時(shí)候有被回收的對象 do { ref = queue.poll() as KeyedWeakReference? if (ref != null) { //如果對象被回收了 ,則講弱引用從待觀察的map里面移除 watchedObjects.remove(ref.key) } } while (ref != null) }
這個(gè)方法里面代碼很少,這里面做的工作就是判斷queue里面有沒有被回收的對象,如果有則將改對象的弱引用從待觀察的集合里面移除。
然后在來看下延時(shí)處理里面做了什么事:
@Synchronized private fun moveToRetained(key: String) { removeWeaklyReachableObjects() val retainedRef = watchedObjects[key] if (retainedRef != null) { retainedRef.retainedUptimeMillis = clock.uptimeMillis() onObjectRetainedListeners.forEach { it.onObjectRetained() } } }
可以看到里面一開始又調(diào)用了一次removeWeaklyReachableObjects()
方法。這邊是第二次調(diào)用了,嘗試移除這5秒內(nèi)已經(jīng)被回收的對象。如果此時(shí)對象仍沒有被回收,也就是還在待觀察集合里面,那么就開始進(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)聽,因?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() } } ... }
接下來在進(jìn)入到heapDumpTrigger.scheduleRetainedObjectCheck()
方法里面
fun scheduleRetainedObjectCheck( delayMillis: Long = 0L ) { val checkCurrentlyScheduledAt = checkScheduledAt //如果之前的子線程任務(wù)來沒開始執(zhí)行則返回,也就是就是下面的backgroundHandler.postDelayed if (checkCurrentlyScheduledAt > 0) { return } checkScheduledAt = SystemClock.uptimeMillis() + delayMillis //讓子線程延時(shí)去delayMillis執(zhí)行,通過上面的調(diào)用鏈進(jìn)入此方法則delayMillis為0,因此會(huì)馬上執(zhí)行 backgroundHandler.postDelayed({ //子線程任務(wù)開始執(zhí)行,就將checkScheduledAt設(shè)置為0,以便下一次出現(xiàn)內(nèi)存泄漏時(shí)能還能進(jìn)來 checkScheduledAt = 0 checkRetainedObjects() }, delayMillis) }
接下來在進(jìn)入到checkRetainedObjects()
這個(gè)方法里面去。由于上面是通過子線程Handler去post,因此該方法是運(yùn)行在子線程里面
private fun checkRetainedObjects() { ... //看下此時(shí)待觀察的集合里面還有多少個(gè)沒有被分析的弱引用對象 //也就是retainedUptimeMillis != -1L 的對象的個(gè)數(shù) //并且在調(diào)用retainedObjectCount.get()的時(shí)候,還會(huì)再調(diào)用一次removeWeaklyReachableObjects(),嘗試再次移除一遍 var retainedReferenceCount = objectWatcher.retainedObjectCount //如果此時(shí)還有泄漏對象,則調(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í)間過短,則會(huì)重新調(diào)用scheduleRetainedObjectCheck, //并延遲執(zhí)行,這個(gè)延遲的時(shí)間 = 60s - 時(shí)間差,避免兩次dump的時(shí)間過短,影響使用 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" //這邊開始進(jìn)行dump dumpHeap( retainedReferenceCount = retainedReferenceCount, retry = true, reason = "$retainedReferenceCount retained objects, app is $visibility" ) }
在這個(gè)checkRetainedObjects()
方法里面執(zhí)行的步驟:
- 先獲取待觀察集合里面被還沒有被分析的疑似泄漏的弱引用對象的個(gè)數(shù),通過
retainedUptimeMillis != -1L
這個(gè)判斷,因?yàn)楫?dāng)對象被通過dump分析之后,會(huì)將retainedUptimeMillis這個(gè)時(shí)間改為-1。而且在objectWatcher.retainedObjectCount
這個(gè)get的方法里面還會(huì)執(zhí)行一次removeWeaklyReachableObjects()
,嘗試再次移除被回收的弱引用對象。 - 如果此時(shí)還是有泄漏對象,也就是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。
- 接下來在判斷下當(dāng)前時(shí)間距離上一次dump的時(shí)間,如果時(shí)間過短(小于60S),則不進(jìn)行dump,而是重新調(diào)用
scheduleRetainedObjectCheck
并延時(shí)執(zhí)行,延遲時(shí)間為:60秒 - 時(shí)間差。并且這邊會(huì)返回,不再執(zhí)行之后的dump - 最后執(zhí)行dump,將內(nèi)存泄漏的案發(fā)現(xiàn)場保存下來,并解析。
接下來來看下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之后處理以分析的對象 objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis) }
這邊注意下:objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
這個(gè)方法,上面我們說到當(dāng)對象被通過dump分析之后,會(huì)將retainedUptimeMillis這個(gè)時(shí)間改為-1。現(xiàn)在我們來看下這個(gè)方法里面的源碼:
/** * Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on * [clock] [Clock.uptimeMillis]) */ @Synchronized fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) { //從待觀察集合里面過濾出時(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ì)過濾出已經(jīng)被dump過的弱引用對象,因?yàn)楫?dāng)它的watchUptimeMillis <= heapDumpUptimeMillis時(shí),dump會(huì)將內(nèi)存中當(dāng)前時(shí)間點(diǎn)之前的疑似泄漏的對象都列舉出來,所以只要是在當(dāng)前dump時(shí)間點(diǎn)之前加入的都可以認(rèn)為是已經(jīng)dump了。
然后接下調(diào)用過濾出來的弱引用對象的claer()
方法,將watchUptimeMillis
時(shí)間設(shè)置為-1,并且從待觀察集合里面移除。
總結(jié)
經(jīng)過了上面的源碼分析,接下來來總結(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)聽器Watcher,分別是Activity、Fragment、Service和RootView。 - 在構(gòu)建4種Watcher時(shí),順便創(chuàng)建了
objectWatcher
,用于之后延時(shí)檢查對象是否被回收。當(dāng)被被觀察的類型在生命周期進(jìn)入結(jié)束時(shí),會(huì)調(diào)用到objectWatcher.expectWeaklyReachable()
方法里面。 - 在
objectWatcher.expectWeaklyReachable()
方法里面會(huì)先將被回收的對象移除待觀察集合,然后創(chuàng)建一個(gè)弱引用放入到待觀察列表里面,最后在開啟延時(shí)檢查對象是否被回收,默認(rèn)延時(shí)5秒。 - 延時(shí)計(jì)時(shí)到了之后會(huì)調(diào)用
objectWatcher.moveToRetained
方法,在該方法內(nèi)會(huì)再次嘗試移除這5秒內(nèi)已經(jīng)被回收的對象,然后調(diào)用InternalLeakCanary.onObjectRetained()
方法,跟著調(diào)用棧進(jìn)入到heapDumpTrigger.scheduleRetainedObjectCheck()
方法,最終到了checkRetainedObjects()
這個(gè)方法里面。 - 在
checkRetainedObjects()
這個(gè)方法里面,會(huì)先判斷待觀察結(jié)合里面是否還有疑似泄漏的弱引用對象,如果有則嘗試進(jìn)行一次GC操作。然后判斷應(yīng)用是在前臺(tái)還是后臺(tái),如果實(shí)在前臺(tái)則當(dāng)疑似泄漏的弱引用對象數(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í)開啟任務(wù),延時(shí)時(shí)間為:60秒 - 兩次dump的時(shí)間差。 - 最后進(jìn)行dump,和dump文件的數(shù)據(jù)分析。dump完成后,會(huì)將待觀察列表里面的已被分析過的弱引用對象的
watchUptimeMillis
時(shí)間設(shè)置為-1,并移除。
以上就是LeakCanary 2.9.1的源碼解析,更多關(guān)于內(nèi)存泄漏檢測LeakCanary的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android超詳細(xì)講解組件LinearLayout的使用
LinearLayout又稱作線性布局,是一種非常常用的布局。正如它的名字所描述的一樣,這個(gè)布局會(huì)將它所包含的控件在線性方向上依次排列。既然是線性排列,肯定就不僅只有一個(gè)方向,這里一般只有兩個(gè)方向:水平方向和垂直方向2022-03-03關(guān)于Touch Panel AA區(qū)要做外擴(kuò)的原因解析
今天小編就為大家分享一篇關(guān)于Touch Panel AA區(qū)要做外擴(kuò)的原因解析,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12RecyclerView實(shí)現(xiàn)拖拽排序效果
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)拖拽排序效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android實(shí)用小技巧之利用Lifecycle寫出更好維護(hù)的代碼
lifecycle是一個(gè)類,用于存儲(chǔ)有關(guān)組件(如Activity或Fragment)的生命周期狀態(tài)的信息,并允許其他對象觀察此狀態(tài),下面這篇文章主要給大家介紹了關(guān)于Android實(shí)用小技巧之利用Lifecycle寫出更好維護(hù)的代碼的相關(guān)資料,需要的朋友可以參考下2022-05-05Android app開發(fā)中Retrofit框架的初步上手使用
這篇文章主要介紹了Android app開發(fā)中Retrofit框架的初步上手使用,Retrofit 2.0發(fā)布以來獲得了巨大的人氣增長,并且經(jīng)常被開發(fā)者們拿來與Volley比較,需要的朋友可以參考下2016-02-02Android ListView列表控件的介紹和性能優(yōu)化
這篇文章主要介紹了Android ListView列表控件的介紹和性能優(yōu)化,需要的朋友可以參考下2017-06-06Android編程實(shí)現(xiàn)動(dòng)態(tài)支持多語言的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)動(dòng)態(tài)支持多語言的方法,涉及Android資源、控件及屬性相關(guān)操作技巧,需要的朋友可以參考下2017-06-06