本文主要是介绍LeakCanary 2.7 原理分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- 1. LeakCanary 初始化
- 2. Watcher
- 2.1 ActivityWatcher
- 2.2 FragmentAndViewModelWatcher
- 2.3 RootViewWatcher
- 2.4 ServiceWatcher
- 2.5 OnObjectRetainedListener
- 2.6 ObjectWatcher
- 3. 寻找泄露对象-ObjectWatcher
- 3.1 检测内存泄漏的时机
- 3.2 需要被检测是否内存泄露的对象 -- WeakReferences 何时添加至监听队列?
- 3.3 寻找可能的泄漏对象
- 3.4 检测泄漏的对象
- 4. 分析 Hprof
- 5. 总结
- 5.1 内存泄露的观察对象
- 5.2 如何触发检测
- 5.3 checkRetainedInstances() 方法去确认泄露
- 5.4 为什么不能用于线上
- 6. 参考链接
1. LeakCanary 初始化
我们直接引入依赖即可,无需代码初始化,LeakCanary
会在 app 初始化时自动初始化自身。
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
Application->attachBaseContext
=====>ContentProvider->onCreate
=====>Application->onCreate
=====>Activity->onCreate
/*** Content providers are loaded before the application class is created. [AppWatcherInstaller] is* used to install [leakcanary.AppWatcher] on application start.*/
internal sealed class AppWatcherInstaller : ContentProvider() {override fun onCreate(): Boolean {val application = context!!.applicationContext as ApplicationAppWatcher.manualInstall(application)return true}......
AppWatcher
是初始化观察对象的入口处:
@JvmOverloadsfun manualInstall(application: Application,retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)) {checkMainThread()..//此处 watchersToInstall 可根据需要自定义 --> eg://watchedObject !is BadSdkLeakingFragment//it !is RootViewWatcherwatchersToInstall.forEach {it.install()}}
manualInstall
方法的参数 watchersToInstall
默认实现 Activity
、Fragment
、View
、ViewModel
和 Service
的监听:
fun appDefaultWatchers(application: Application,reachabilityWatcher: ReachabilityWatcher = objectWatcher): List<InstallableWatcher> {return listOf(ActivityWatcher(application, reachabilityWatcher),FragmentAndViewModelWatcher(application, reachabilityWatcher),RootViewWatcher(reachabilityWatcher),ServiceWatcher(reachabilityWatcher))}
2. Watcher
2.1 ActivityWatcher
/*** Expects activities to become weakly reachable soon after they receive the [Activity.onDestroy]* callback.*/
class ActivityWatcher(private val application: Application,private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {private val lifecycleCallbacks =object : Application.ActivityLifecycleCallbacks by noOpDelegate() {override fun onActivityDestroyed(activity: Activity) {reachabilityWatcher.expectWeaklyReachable(activity, "${activity::class.java.name} received Activity#onDestroy() callback")}}//LeakCanary init时调用override fun install() {application.registerActivityLifecycleCallbacks(lifecycleCallbacks)}//没找到调用此方法的地方override fun uninstall() {application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)}
}
通过 application
监听 Activity
的生命周期,在 onDestory
回调reachabilityWatcher.expectWeaklyReachable
方法,做内存泄漏的检查工作。
2.2 FragmentAndViewModelWatcher
FragmentAndViewModelWatcher
主要检测了三种
- Fragments (Support Library, Android X and AOSP)
- Fragment#onDestroy()
- Fragment views (Support Library, Android X and AOSP)
- Fragment#onDestroyView()
- Android X view models (both activity and fragment view models)
- ViewModel#onCleared()
/*** Expects:* - Fragments (Support Library, Android X and AOSP) to become weakly reachable soon after they* receive the Fragment#onDestroy() callback.* - Fragment views (Support Library, Android X and AOSP) to become weakly reachable soon after* fragments receive the Fragment#onDestroyView() callback.* - Android X view models (both activity and fragment view models) to become weakly reachable soon* after they received the ViewModel#onCleared() callback.*/
class FragmentAndViewModelWatcher(private val application: Application,private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()if (SDK_INT >= O) {fragmentDestroyWatchers.add(AndroidOFragmentDestroyWatcher(reachabilityWatcher))}getWatcherIfAvailable(ANDROIDX_FRAGMENT_CLASS_NAME,ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,reachabilityWatcher)?.let {fragmentDestroyWatchers.add(it)}getWatcherIfAvailable(ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,reachabilityWatcher)?.let {fragmentDestroyWatchers.add(it)}fragmentDestroyWatchers}private val lifecycleCallbacks =object : Application.ActivityLifecycleCallbacks by noOpDelegate() {override fun onActivityCreated(activity: Activity,savedInstanceState: Bundle?) {for (watcher in fragmentDestroyWatchers) {watcher(activity)}}}override fun install() {application.registerActivityLifecycleCallbacks(lifecycleCallbacks)}override fun uninstall() {application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)}...
- AndroidOFragmentDestroyWatcher
internal class AndroidOFragmentDestroyWatcher(private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {override fun onFragmentViewDestroyed(fm: FragmentManager,fragment: Fragment) {val view = fragment.viewif (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) {val fragmentManager = activity.fragmentManagerfragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)}
2.3 RootViewWatcher
Expects root views to become weakly reachable soon after they are removed from the window
通过 OnAttachStateChangeListener
的 onViewAttachedToWindow
和onViewDetachedFromWindow
方法回调可做内存泄漏的检查工作:
/*** Expects root views to become weakly reachable soon after they are removed from the window* manager.*/
class RootViewWatcher(private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {private val listener = OnRootViewAddedListener { rootView ->val trackDetached = when(rootView.windowType) {PHONE_WINDOW -> {when (rootView.phoneWindow?.callback?.wrappedCallback) {// Activities are already tracked by ActivityWatcheris Activity -> falseis Dialog -> rootView.resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)// Probably a DreamServiceelse -> true}}// Android widgets keep detached popup window instances around.POPUP_WINDOW -> falseTOOLTIP, 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) {mainHandler.removeCallbacks(watchDetachedView)}override fun onViewDetachedFromWindow(v: View) {mainHandler.post(watchDetachedView)}})}}override fun install() {Curtains.onRootViewsChangedListeners += listener}override fun uninstall() {Curtains.onRootViewsChangedListeners -= listener}
}
2.4 ServiceWatcher
Service
通过hook ActivityThread的 H
类和 AMS
,当 AMS
调用 serviceDoneExecuting
方法可做内存泄漏的检查工作。
/*** Expects services to become weakly reachable soon after they receive the [Service.onDestroy]* callback.*/
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()...override fun install() {checkMainThread()try {//hook ActivityThread中H类的Callback swapActivityThreadHandlerCallback { mCallback ->..Handler.Callback { msg ->if (msg.what == STOP_SERVICE) {val key = msg.obj as IBinder//在收到STOP_SERVICE时将Service以弱应用形式记录activityThreadServices[key]?.let {onServicePreDestroy(key, it)}}mCallback?.handleMessage(msg) ?: false}}//这里hook了AMSswapActivityManager { activityManagerInterface, activityManagerInstance ->...//通过代理形式 在调用serviceDoneExecuting方法时可判断对象是否泄漏Proxy.newProxyInstance(activityManagerInterface.classLoader, arrayOf(activityManagerInterface)) { _, method, args ->//serviceDoneExecuting is METHOD_SERVICE_DONE_EXECUTING if (METHOD_SERVICE_DONE_EXECUTING == method.name) {val token = args!![0] as IBinderif (servicesToBeDestroyed.containsKey(token)) {onServiceDestroyed(token)}}...//method.invoke(activityManagerInstance)}}} catch (ignored: Throwable) {SharkLog.d(ignored) { "Could not watch destroyed services" }}}private fun onServicePreDestroy(token: IBinder,service: Service) {servicesToBeDestroyed[token] = WeakReference(service)}private fun onServiceDestroyed(token: IBinder) {servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->serviceWeakReference.get()?.let { service ->reachabilityWatcher.expectWeaklyReachable(service, "${service::class.java.name} received Service#onDestroy() callback")}}}//省略 hook 代码companion object {private const val STOP_SERVICE = 116private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"}override fun uninstall() {checkMainThread()uninstallActivityManager?.invoke()uninstallActivityThreadHandlerCallback?.invoke()uninstallActivityManager = nulluninstallActivityThreadHandlerCallback = null}
}
2.5 OnObjectRetainedListener
Listener used by [ObjectWatcher] to report retained objects.
/*** Listener used by [ObjectWatcher] to report retained objects.*/
fun interface OnObjectRetainedListener {/*** A watched object became retained.*/fun onObjectRetained()companion object {inline operator fun invoke(crossinline block: () -> Unit): OnObjectRetainedListener =object : OnObjectRetainedListener {override fun onObjectRetained() {block()}}}
}
- InternalLeakCanary#invoke
此回调中会初始化一些检测内存泄露过程中需要的对象:
override fun invoke(application: Application) {_application = applicationcheckRunningInDebuggableBuild()AppWatcher.objectWatcher.addOnObjectRetainedListener(this)val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))val gcTrigger = GcTrigger.Default// 配置项val configProvider = { LeakCanary.config }// 发起内存泄漏检测的线程val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)handlerThread.start()val backgroundHandler = Handler(handlerThread.looper)heapDumpTrigger = HeapDumpTrigger(application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,configProvider)application.registerVisibilityListener { applicationVisible ->this.applicationVisible = applicationVisibleheapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)}registerResumedActivityListener(application)addDynamicShortcut(application)// We post so that the log happens after Application.onCreate()mainHandler.post {// https://github.com/square/leakcanary/issues/1981// We post to a background handler because HeapDumpControl.iCanHasHeap() checks a shared pref// which blocks until loaded and that creates a StrictMode violation.backgroundHandler.post {SharkLog.d {...}}}}
- heapDumper 用于确认内存泄漏之后进行 heap dump 工作。
- gcTrigger 用于发现可能的内存泄漏之后手动调用 GC 确认是否真的为内存泄露。
这两个对象是 LeakCanary 检测内存泄漏的核心
2.6 ObjectWatcher
/*** [ObjectWatcher] can be passed objects to [watch]. It will create [KeyedWeakReference] instances* that reference watches objects, and check if those references have been cleared as expected on* the [checkRetainedExecutor] executor. If not, these objects are considered retained and* [ObjectWatcher] will then notify registered [OnObjectRetainedListener]s on that executor thread.** [checkRetainedExecutor] is expected to run its tasks on a background thread, with a significant* delay to give the GC the opportunity to identify weakly reachable objects.** [ObjectWatcher] is thread safe.*/
// Thread safe by locking on all methods, which is reasonably efficient given how often
// these methods are accessed.
class ObjectWatcher constructor(private val clock: Clock,private val checkRetainedExecutor: Executor,/*** Calls to [watch] will be ignored when [isEnabled] returns false*/private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()/*** References passed to [watch].*/private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()private val queue = ReferenceQueue<Any>()/*** Returns true if there are watched objects that aren't weakly reachable, and* have been watched for long enough to be considered retained.*/val hasRetainedObjects: Boolean@Synchronized get() {removeWeaklyReachableObjects()return watchedObjects.any { it.value.retainedUptimeMillis != -1L }}/*** Returns the number of retained objects, ie the number of watched objects that aren't weakly* reachable, and have been watched for long enough to be considered retained.*/val retainedObjectCount: Int@Synchronized get() {removeWeaklyReachableObjects()return watchedObjects.count { it.value.retainedUptimeMillis != -1L }}/*** Returns true if there are watched objects that aren't weakly reachable, even* if they haven't been watched for long enough to be considered retained.*/val hasWatchedObjects: Boolean@Synchronized get() {removeWeaklyReachableObjects()return watchedObjects.isNotEmpty()}/*** Returns the objects that are currently considered retained. Useful for logging purposes.* Be careful with those objects and release them ASAP as you may creating longer lived leaks* then the one that are already there.*/val retainedObjects: List<Any>@Synchronized get() {removeWeaklyReachableObjects()val instances = mutableListOf<Any>()for (weakReference in watchedObjects.values) {if (weakReference.retainedUptimeMillis != -1L) {val instance = weakReference.get()if (instance != null) {instances.add(instance)}}}return instances}@Synchronized fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) {onObjectRetainedListeners.add(listener)}@Synchronized fun removeOnObjectRetainedListener(listener: OnObjectRetainedListener) {onObjectRetainedListeners.remove(listener)}/*** Identical to [watch] with an empty string reference name.*/@Deprecated("Add description parameter explaining why an object is watched to help understand leak traces.",replaceWith = ReplaceWith("expectWeaklyReachable(watchedObject, \"Explain why this object should be garbage collected soon\")"))fun watch(watchedObject: Any) {expectWeaklyReachable(watchedObject, "")}@Deprecated("Method renamed expectWeaklyReachable() to clarify usage.",replaceWith = ReplaceWith("expectWeaklyReachable(watchedObject, description)"))fun watch(watchedObject: Any,description: String) {expectWeaklyReachable(watchedObject, description)}@Synchronized override fun expectWeaklyReachable(watchedObject: Any,description: String) {if (!isEnabled()) {return}removeWeaklyReachableObjects()val key = UUID.randomUUID().toString()val watchUptimeMillis = clock.uptimeMillis()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] = referencecheckRetainedExecutor.execute {moveToRetained(key)}}/*** Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on* [clock] [Clock.uptimeMillis])*/@Synchronized fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {val weakRefsToRemove =watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis }weakRefsToRemove.values.forEach { it.clear() }watchedObjects.keys.removeAll(weakRefsToRemove.keys)}/*** Clears all [KeyedWeakReference]*/@Synchronized fun clearWatchedObjects() {watchedObjects.values.forEach { it.clear() }watchedObjects.clear()}@Synchronized private fun moveToRetained(key: String) {removeWeaklyReachableObjects()val retainedRef = watchedObjects[key]if (retainedRef != null) {retainedRef.retainedUptimeMillis = clock.uptimeMillis()onObjectRetainedListeners.forEach { it.onObjectRetained() }}}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?do {ref = queue.poll() as KeyedWeakReference?if (ref != null) {watchedObjects.remove(ref.key)}} while (ref != null)}
}
3. 寻找泄露对象-ObjectWatcher
当对象在恰当的时机回收后,通过 ObjectWatcher
的expectWeaklyReachable
方法进行观察是否有泄露
3.1 检测内存泄漏的时机
- ActivityWatcher
- Activity#onActivityDestroyed
- FragmentAndViewModelWatcher
- Fragments (Support Library, Android X and AOSP)
- Fragment#onDestroy()
- Fragment views (Support Library, Android X and AOSP)
- Fragment#onDestroyView()
- Android X view models (both activity and fragment view models)
- ViewModel#onCleared()
- Fragments (Support Library, Android X and AOSP)
Expects root views to become weakly reachable soon after they are removed from the window
- RootViewWatcher
- rootView.addOnAttachStateChangeListener#onViewDetachedFromWindow
- ServiceWatcher
- AMS#serviceDoneExecuting
3.2 需要被检测是否内存泄露的对象 – WeakReferences 何时添加至监听队列?
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.
//主要看上面描述就够了
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?do {ref = queue.poll() as KeyedWeakReference?if (ref != null) {watchedObjects.remove(ref.key)}} while (ref != null)}
3.3 寻找可能的泄漏对象
@Synchronized override fun expectWeaklyReachable(watchedObject: Any,description: String) {...//移除非泄漏的弱引用对象removeWeaklyReachableObjects()//为此对象生成唯一随机idval key = UUID.randomUUID().toString()val watchUptimeMillis = clock.uptimeMillis()val reference =KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)...//将此对象包成weakRefrence对象 然后加入队列中watchedObjects[key] = reference//5s后弱引用依然存在则有泄漏风险checkRetainedExecutor.execute {moveToRetained(key)}}
3.4 检测泄漏的对象
- HeapDumpTrigger
private fun checkRetainedObjects() {...var retainedReferenceCount = objectWatcher.retainedObjectCount//存在泄漏对象的话手动GC一次 然后再次检测if (retainedReferenceCount > 0) {gcTrigger.runGc()//GC 后重新计算 retainedReferenceCount retainedReferenceCount = objectWatcher.retainedObjectCount}//如果泄漏数量不超出阈值(默认5) 结束if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return//泄漏数量 > 阈值(默认5),存在内存泄漏,生成hprof文件val now = SystemClock.uptimeMillis()val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillisif (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"//dump文件 通过原生Debug.dumpHprofData(heapDumpFile.absolutePath);dumpHeap(retainedReferenceCount = retainedReferenceCount,retry = true,reason = "$retainedReferenceCount retained objects, app is $visibility")}
- retainedReferenceCount
/*** Returns the number of retained objects, ie the number of watched objects that aren't weakly* reachable, and have been watched for long enough to be considered retained.*/val retainedObjectCount: Int@Synchronized get() {removeWeaklyReachableObjects()return watchedObjects.count { it.value.retainedUptimeMillis != -1L }}
- 这一步并不会立刻触发
dump
生成hprof
文件分析,而是会先触发GC回收对象,再次判断未回收的对象数量 - 如果超出5个,通过原生方式生成hprof文件,再通过HeapAnalyzerService分析这个文件。
HeapAnalyzerService属于是square的另一个开源库shark,是一个前台服务,整个流程通过analyzeHeap方法分析文件,将分析出的结果包装成一个HeapAnalysisSuccess对象通过onHeapAnalyzedListener回调。
4. 分析 Hprof
hprof文件的标准协议主要由head和body组成,body是由一系列不同类型的Record组成,Record主要用于描述trace、object、thread等信息,依次分为4个部分:TAG、TIME、LENGTH、BODY,其中TAG就是表示Record类型。Record之间依次排列或嵌套,最终组成hprof文件。
多个Record被进抽象为HprofMemoryIndex,Index可以快速定位到对应对象在hprof文件中的位置;最终Index和Hprof一起再组成HprofGraph,graph做为hprof的最上层描述,将所有堆中数据抽象为了 gcRoots、objects、classes、instances等集合。
interface HeapGraph {val gcRoots: List<GcRoot>val objects: Sequence<HeapObject>val classes: Sequence<HeapClass>val instances: Sequence<HeapInstance>val objectArrays: Sequence<HeapObjectArray>...
}
后续通过 Graph
的 objects
集合找出泄露对象,然后通过广度优先遍历找出其到 GcRoot
的引用路径链,结束流程。
5. 总结
5.1 内存泄露的观察对象
不再需要的对象依然被引用,导致对象被分配的内存无法被回收 --> GC 回收机制
默认情况下 LeakCanary
会观察以下对象:
Activity
Fragment
RootView
Service
- ActivityWatcher
- Activity#onActivityDestroyed
- FragmentAndViewModelWatcher
- Fragments (Support Library, Android X and AOSP)
- Fragment#onDestroy()
- Fragment views (Support Library, Android X and AOSP)
- Fragment#onDestroyView()
- Android X view models (both activity and fragment view models)
- ViewModel#onCleared()
- Fragments (Support Library, Android X and AOSP)
- RootViewWatcher
- rootView.addOnAttachStateChangeListener#onViewDetachedFromWindow
- ServiceWatcher
- AMS#serviceDoneExecuting
5.2 如何触发检测
Activity
举例- app 运行时会 registerActivityLifecycleCallbacks
Activity
onDestory
调用reachabilityWatcher.expectWeaklyReachable
去检测内存泄漏
private val lifecycleCallbacks =object : Application.ActivityLifecycleCallbacks by noOpDelegate() {override fun onActivityDestroyed(activity: Activity) {reachabilityWatcher.expectWeaklyReachable(activity, "${activity::class.java.name} received Activity#onDestroy() callback")}}
ActivityWatcher
fun interface ReachabilityWatcher {/*** Expects the provided [watchedObject] to become weakly reachable soon. If not,* [watchedObject] will be considered retained.*/fun expectWeaklyReachable(watchedObject: Any,description: String)
}
- ObjectWatcher#expectWeaklyReachable
@Synchronized override fun expectWeaklyReachable(watchedObject: Any,description: String) {if (!isEnabled()) {return}removeWeaklyReachableObjects()val key = UUID.randomUUID().toString()val watchUptimeMillis = clock.uptimeMillis()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] = referencecheckRetainedExecutor.execute {moveToRetained(key)}}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?do {ref = queue.poll() as KeyedWeakReference?if (ref != null) {watchedObjects.remove(ref.key)}} while (ref != null)}
可以看出
- 传入的观察对象都会被存储在watchedObjects中
- 会为每个watchedObject生成一个KeyedWeakReference弱引用对象并与一个queue关联,当对象被回收时,该弱引用对象将进入queue当中
- 在检测过程中,我们会调用多次removeWeaklyReachableObjects,将已回收对象从watchedObjects中移除
- 如果watchedObjects中没有移除对象,证明它没有被回收,那么就会调用moveToRetained
5.3 checkRetainedInstances() 方法去确认泄露
- ObjectWatcher#moveToRetained
@Synchronized private fun moveToRetained(key: String) {removeWeaklyReachableObjects()val retainedRef = watchedObjects[key]if (retainedRef != null) {retainedRef.retainedUptimeMillis = clock.uptimeMillis()onObjectRetainedListeners.forEach { it.onObjectRetained() }}}
- OnObjectRetainedListener#onObjectRetained
/*** Listener used by [ObjectWatcher] to report retained objects.** This is a functional interface with which you can create a [OnObjectRetainedListener] from a lambda.*/
fun interface OnObjectRetainedListener {/*** A watched object became retained.*/fun onObjectRetained()...
}
- InternalLeakCanary#
override fun onObjectRetained() = scheduleRetainedObjectCheck()fun scheduleRetainedObjectCheck() {if (this::heapDumpTrigger.isInitialized) {heapDumpTrigger.scheduleRetainedObjectCheck()}}
- HeapDumpTrigger#scheduleRetainedObjectCheck
fun scheduleRetainedObjectCheck(delayMillis: Long = 0L) {val checkCurrentlyScheduledAt = checkScheduledAtif (checkCurrentlyScheduledAt > 0) {return}checkScheduledAt = SystemClock.uptimeMillis() + delayMillisbackgroundHandler.postDelayed({checkScheduledAt = 0checkRetainedObjects()}, delayMillis)}
private fun checkRetainedObjects() {val iCanHasHeap = HeapDumpControl.iCanHasHeap()val config = configProvider()if (iCanHasHeap is Nope) {if (iCanHasHeap is NotifyingNope) {// Before notifying that we can't dump heap, let's check if we still have retained object.var retainedReferenceCount = objectWatcher.retainedObjectCountif (retainedReferenceCount > 0) {gcTrigger.runGc()retainedReferenceCount = objectWatcher.retainedObjectCount}val nopeReason = iCanHasHeap.reason()val wouldDump = !checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold, nopeReason)if (wouldDump) {val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))showRetainedCountNotification(objectCount = retainedReferenceCount,contentText = uppercaseReason)}}...return}var retainedReferenceCount = objectWatcher.retainedObjectCountif (retainedReferenceCount > 0) {gcTrigger.runGc()retainedReferenceCount = objectWatcher.retainedObjectCount}if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) returnval now = SystemClock.uptimeMillis()val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillisif (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"dumpHeap(retainedReferenceCount = retainedReferenceCount,retry = true,reason = "$retainedReferenceCount retained objects, app is $visibility")}
- 如果retainedObjectCount数量大于0,则进行一次GC,避免额外的Dump
- 默认情况下,如果retainedReferenceCount<5,不会进行Dump,节省资源
- 如果两次Dump之间时间少于60s,也会直接返回,避免频繁Dump
- 调用heapDumper.dumpHeap()进行真正的Dump操作
- Dump之后,要删除已经处理过了的引用
- 调用HeapAnalyzerService.runAnalysis对结果进行分析
对象应该被销毁时观察,eg: Activity onDestory() 时添加至 WeakRefrence 观察队列(参考3.3),五秒后该对象还在观察队列中代表可能存在内存泄漏
5.4 为什么不能用于线上
- 每次内存泄漏以后,都会生成一个.hprof文件,然后解析,并将结果写入.hprof.result。增加手机负担,引起手机卡顿等问题。
- 多次调用GC,可能会对线上性能产生影响
- 同样的泄漏问题,会重复生成 .hprof 文件,重复分析并写入磁盘。
- hprof文件较大,信息回捞成问题。
6. 参考链接
- 全新 LeakCanary 2 ! 完全基于 Kotlin 重构升级 !
- From Erren - GC 回收机制
- LeakCanary 新版 2.x ,你应该知道的知识点
这篇关于LeakCanary 2.7 原理分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!