LeakCanary 2.7 原理分析

2024-05-28 11:32
文章标签 分析 原理 2.7 leakcanary

本文主要是介绍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 默认实现 ActivityFragmentViewViewModelService 的监听:

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

通过 OnAttachStateChangeListeneronViewAttachedToWindowonViewDetachedFromWindow 方法回调可做内存泄漏的检查工作:

/*** 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

当对象在恰当的时机回收后,通过 ObjectWatcherexpectWeaklyReachable 方法进行观察是否有泄露

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()

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>...
}

后续通过 Graphobjects 集合找出泄露对象,然后通过广度优先遍历找出其到 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()
  • 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)}

可以看出

  1. 传入的观察对象都会被存储在watchedObjects中
  2. 会为每个watchedObject生成一个KeyedWeakReference弱引用对象并与一个queue关联,当对象被回收时,该弱引用对象将进入queue当中
  3. 在检测过程中,我们会调用多次removeWeaklyReachableObjects,将已回收对象从watchedObjects中移除
  4. 如果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")}
  1. 如果retainedObjectCount数量大于0,则进行一次GC,避免额外的Dump
  2. 默认情况下,如果retainedReferenceCount<5,不会进行Dump,节省资源
  3. 如果两次Dump之间时间少于60s,也会直接返回,避免频繁Dump
  4. 调用heapDumper.dumpHeap()进行真正的Dump操作
  5. Dump之后,要删除已经处理过了的引用
  6. 调用HeapAnalyzerService.runAnalysis对结果进行分析

对象应该被销毁时观察,eg: Activity onDestory() 时添加至 WeakRefrence 观察队列(参考3.3),五秒后该对象还在观察队列中代表可能存在内存泄漏

在这里插入图片描述

5.4 为什么不能用于线上

  1. 每次内存泄漏以后,都会生成一个.hprof文件,然后解析,并将结果写入.hprof.result。增加手机负担,引起手机卡顿等问题。
  2. 多次调用GC,可能会对线上性能产生影响
  3. 同样的泄漏问题,会重复生成 .hprof 文件,重复分析并写入磁盘。
  4. hprof文件较大,信息回捞成问题。

6. 参考链接

  • 全新 LeakCanary 2 ! 完全基于 Kotlin 重构升级 !
  • From Erren - GC 回收机制
  • LeakCanary 新版 2.x ,你应该知道的知识点

这篇关于LeakCanary 2.7 原理分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1010440

相关文章

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

MySQL中的MVCC底层原理解读

《MySQL中的MVCC底层原理解读》本文详细介绍了MySQL中的多版本并发控制(MVCC)机制,包括版本链、ReadView以及在不同事务隔离级别下MVCC的工作原理,通过一个具体的示例演示了在可重... 目录简介ReadView版本链演示过程总结简介MVCC(Multi-Version Concurr

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

SpringCloud配置动态更新原理解析

《SpringCloud配置动态更新原理解析》在微服务架构的浩瀚星海中,服务配置的动态更新如同魔法一般,能够让应用在不重启的情况下,实时响应配置的变更,SpringCloud作为微服务架构中的佼佼者,... 目录一、SpringBoot、Cloud配置的读取二、SpringCloud配置动态刷新三、更新@R

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专