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

相关文章

MySQL中的LENGTH()函数用法详解与实例分析

《MySQL中的LENGTH()函数用法详解与实例分析》MySQLLENGTH()函数用于计算字符串的字节长度,区别于CHAR_LENGTH()的字符长度,适用于多字节字符集(如UTF-8)的数据验证... 目录1. LENGTH()函数的基本语法2. LENGTH()函数的返回值2.1 示例1:计算字符串

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

深度解析Spring AOP @Aspect 原理、实战与最佳实践教程

《深度解析SpringAOP@Aspect原理、实战与最佳实践教程》文章系统讲解了SpringAOP核心概念、实现方式及原理,涵盖横切关注点分离、代理机制(JDK/CGLIB)、切入点类型、性能... 目录1. @ASPect 核心概念1.1 AOP 编程范式1.2 @Aspect 关键特性2. 完整代码实

python中Hash使用场景分析

《python中Hash使用场景分析》Python的hash()函数用于获取对象哈希值,常用于字典和集合,不可变类型可哈希,可变类型不可,常见算法包括除法、乘法、平方取中和随机数哈希,各有优缺点,需根... 目录python中的 Hash除法哈希算法乘法哈希算法平方取中法随机数哈希算法小结在Python中,

Java Stream的distinct去重原理分析

《JavaStream的distinct去重原理分析》Javastream中的distinct方法用于去除流中的重复元素,它返回一个包含过滤后唯一元素的新流,该方法会根据元素的hashcode和eq... 目录一、distinct 的基础用法与核心特性二、distinct 的底层实现原理1. 顺序流中的去重

Spring @Scheduled注解及工作原理

《Spring@Scheduled注解及工作原理》Spring的@Scheduled注解用于标记定时任务,无需额外库,需配置@EnableScheduling,设置fixedRate、fixedDe... 目录1.@Scheduled注解定义2.配置 @Scheduled2.1 开启定时任务支持2.2 创建

关于MyISAM和InnoDB对比分析

《关于MyISAM和InnoDB对比分析》:本文主要介绍关于MyISAM和InnoDB对比分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录开篇:从交通规则看存储引擎选择理解存储引擎的基本概念技术原理对比1. 事务支持:ACID的守护者2. 锁机制:并发控制的艺