內(nèi)存泄漏檢測(cè)工具LeakCanary源碼解析
前言
LeakCanary是一個(gè)簡(jiǎn)單方便的內(nèi)存泄漏檢測(cè)工具,它是由大名鼎鼎的Square公司出品并開源的出來的。目前大部分APP在開發(fā)階段都會(huì)接入此工具用來檢測(cè)內(nèi)存泄漏問題。它讓我們開發(fā)者可以在開發(fā)階段就發(fā)現(xiàn)一些沒有注意到或者不規(guī)范的代碼導(dǎo)致的內(nèi)存泄漏,從而就避免了因內(nèi)存泄漏而最終導(dǎo)致OOM的問題。
使用
LeakCanary的使用非常簡(jiǎn)單,我們只需要在項(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對(duì)象泄漏檢查
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
}
...
}
如果對(duì)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ī)。不過這對(duì)于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í)間是用來延遲檢測(cè)被觀察的對(duì)象是否已被釋放,
//也就是說如果到了這個(gè)時(shí)間,對(duì)象仍沒有被釋放,那么可能出現(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è)類是用來檢查判斷對(duì)象是否被回收
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()方法,對(duì)LeakCanary的檢查判斷泄漏的一些類進(jìn)行初始化。接下來會(huì)對(duì)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,后面在對(duì)對(duì)象回收檢查時(shí),會(huì)調(diào)用run方法,通過postDelayed延時(shí)去檢查。
接著上面的appDefaultWatchers方法,里面會(huì)創(chuàng)建四種類型的生命周期監(jiān)聽器,分別是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)
//這邊通過監(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)聽比較簡(jiǎn)單,只需要注冊(cè)一個(gè)回調(diào)即可,因?yàn)锳ndroid本身有提供全局監(jiān)聽Activity的生命周期的回調(diào)。只需要在onActivityDestroyed回調(diào)里面對(duì)對(duì)象回收情況進(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)聽就比較麻煩了,需要對(duì)不同包的Fragment做適配處理,分別是:
android.app.Fragmentandroid.support.v4.app.Fragmentandroidx.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ì)對(duì)不同包下的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去注冊(cè)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)行對(duì)象回收檢查。
在該類里面,我們還能看到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()方法里面通過反射的方式取出ViewModelStore里面的集合,進(jìn)行迭代遍歷,對(duì)集合里面的所有ViewModel開啟對(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)聽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來對(duì)view進(jìn)行狀態(tài)的監(jiān)聽
Curtains.onRootViewsChangedListeners += listener
}
RootView這邊只要監(jiān)聽一些dialog、tooltip、toast等,代碼不長(zhǎng),簡(jiǎn)單說下
- 創(chuàng)建一個(gè)
OnRootViewAddedListener監(jiān)聽,使用的是第三方庫Curtains,在回調(diào)里面拿到rootView; - 對(duì)這個(gè)rootView注冊(cè)一個(gè)
OnAttachStateChangeListener監(jiān)聽,在onViewAttachedToWindow方法里面移除對(duì)象回收檢查。在onViewDetachedFromWindow里面開啟對(duì)象回收檢查;
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對(duì)象
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對(duì)象的回收情況。
Leakcanary對(duì)象泄漏檢查
在被觀察類型的生命周期的結(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ì)先先處理下,將被回收的對(duì)象從watchedObjects這個(gè)待觀察的集合里面移除
removeWeaklyReachableObjects()
//創(chuàng)建一個(gè)隨機(jī)的UUID,用來當(dāng)做待觀察對(duì)象的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)建弱引用對(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ì)象,接著開啟延時(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í)候有被回收的對(duì)象
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
//如果對(duì)象被回收了 ,則講弱引用從待觀察的map里面移除
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
這個(gè)方法里面代碼很少,這里面做的工作就是判斷queue里面有沒有被回收的對(duì)象,如果有則將改對(duì)象的弱引用從待觀察的集合里面移除。
然后在來看下延時(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)被回收的對(duì)象。如果此時(shí)對(duì)象仍沒有被回收,也就是還在待觀察集合里面,那么就開始進(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è)沒有被分析的弱引用對(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í)間過短,則會(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í)行的步驟:
- 先獲取待觀察集合里面被還沒有被分析的疑似泄漏的弱引用對(duì)象的個(gè)數(shù),通過
retainedUptimeMillis != -1L這個(gè)判斷,因?yàn)楫?dāng)對(duì)象被通過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。
- 接下來在判斷下當(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)場(chǎng)保存下來,并解析。
接下來來看下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è)方法,上面我們說到當(dāng)對(duì)象被通過dump分析之后,會(huì)將retainedUptimeMillis這個(gè)時(shí)間改為-1?,F(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過的弱引用對(duì)象,因?yàn)楫?dāng)它的watchUptimeMillis <= heapDumpUptimeMillis時(shí),dump會(huì)將內(nèi)存中當(dāng)前時(shí)間點(diǎn)之前的疑似泄漏的對(duì)象都列舉出來,所以只要是在當(dāng)前dump時(shí)間點(diǎn)之前加入的都可以認(rèn)為是已經(jīng)dump了。
然后接下調(diào)用過濾出來的弱引用對(duì)象的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í)檢查對(duì)象是否被回收。當(dāng)被被觀察的類型在生命周期進(jìn)入結(jié)束時(shí),會(huì)調(diào)用到objectWatcher.expectWeaklyReachable()方法里面。 - 在
objectWatcher.expectWeaklyReachable()方法里面會(huì)先將被回收的對(duì)象移除待觀察集合,然后創(chuàng)建一個(gè)弱引用放入到待觀察列表里面,最后在開啟延時(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í)開啟任務(wù),延時(shí)時(shí)間為:60秒 - 兩次dump的時(shí)間差。 - 最后進(jìn)行dump,和dump文件的數(shù)據(jù)分析。dump完成后,會(huì)將待觀察列表里面的已被分析過的弱引用對(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又稱作線性布局,是一種非常常用的布局。正如它的名字所描述的一樣,這個(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-12
RecyclerView實(shí)現(xiàn)拖拽排序效果
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)拖拽排序效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android實(shí)用小技巧之利用Lifecycle寫出更好維護(hù)的代碼
lifecycle是一個(gè)類,用于存儲(chǔ)有關(guān)組件(如Activity或Fragment)的生命周期狀態(tài)的信息,并允許其他對(duì)象觀察此狀態(tài),下面這篇文章主要給大家介紹了關(guān)于Android實(shí)用小技巧之利用Lifecycle寫出更好維護(hù)的代碼的相關(guān)資料,需要的朋友可以參考下2022-05-05
Android app開發(fā)中Retrofit框架的初步上手使用
這篇文章主要介紹了Android app開發(fā)中Retrofit框架的初步上手使用,Retrofit 2.0發(fā)布以來獲得了巨大的人氣增長(zhǎng),并且經(jīng)常被開發(fā)者們拿來與Volley比較,需要的朋友可以參考下2016-02-02
Android ListView列表控件的介紹和性能優(yōu)化
這篇文章主要介紹了Android ListView列表控件的介紹和性能優(yōu)化,需要的朋友可以參考下2017-06-06
Android編程實(shí)現(xiàn)動(dòng)態(tài)支持多語言的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)動(dòng)態(tài)支持多語言的方法,涉及Android資源、控件及屬性相關(guān)操作技巧,需要的朋友可以參考下2017-06-06

