Principle of Android LeakCanary Memory Leak Detection

  • 2021-12-11 19:11:01
  • OfStack

Directory How to Get context Which class objects are detected for memory leaks by default How to integrate these lifecycle objects into monitoring ActivityWatcher
FragmentAndViewModelWatcher
RootViewWatcherServiceWatcher
How to Determine Objects with Memory Leaks How to Determine the Reference Chain from GC root to the Leaked Object

LeakCanary2.6 source analysis of LeakCanary memory leak detection principle, in order to reduce the length, highlight the key points, do not paste a large number of source code, read with the source code to eat.

How to Get context

LeakCanary can perform memory leak detection only by introducing dependencies and not initializing code. It obtains context of application through ContentProvider. This way of obtaining context is 10 points popular in open source third-party libraries. The following AppWatcherInstaller is registered in the manifest file in the aar package of LeakCanary.


internal sealed class AppWatcherInstaller : ContentProvider() {
 override fun onCreate(): Boolean {
  val application = context!!.applicationContext as Application
  AppWatcher.manualInstall(application)//1
  return true
 }
 ...
}

Which class objects are detected for memory leaks by default

The method at (1) will call the following method to register the object that needs to be detected for leaks:


 fun appDefaultWatchers(
  application: Application,
  reachabilityWatcher: ReachabilityWatcher = objectWatcher
 ): List<InstallableWatcher> {
  return listOf(
   ActivityWatcher(application, reachabilityWatcher),
   FragmentAndViewModelWatcher(application, reachabilityWatcher),
   RootViewWatcher(reachabilityWatcher),
   ServiceWatcher(reachabilityWatcher)
  )
 }

It can be seen that LeakCanary will include Activity, Fragment, ViewModel, RootView and Service in the detection. These objects have a clear life cycle and occupy a high amount of memory, so their memory leaks need our attention.

How to integrate these lifecycle objects into monitoring

The manualInstall method at (1) iterates over the install method that calls the above Watcher to include these lifecycle objects in the detection as appropriate.

ActivityWatcher

The install method in ActivityWatcher realizes the detection of Activity life cycle by registering Application. ActivityLifecycleCallbacks interface callback with application. Here is a great skill, using Kotlin delegate and Java dynamic proxy, will not need to pay attention to the method to give the default empty implementation, (2) (3) code extracted, can be used in the usual development needs of the place.


//ActivityWatcher
 private val lifecycleCallbacks =
  object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
   override fun onActivityDestroyed(activity: Activity) {
    reachabilityWatcher.expectWeaklyReachable(
     activity, "${activity::class.java.name} received Activity#onDestroy() callback"
    )//4
   }
  }

internal inline fun <reified T : Any> noOpDelegate(): T {
 val javaClass = T::class.java
 return Proxy.newProxyInstance(
  javaClass.classLoader, arrayOf(javaClass), NO_OP_HANDLER
 ) as T
}//2

private val NO_OP_HANDLER = InvocationHandler { _, _, _ ->
 // no op
}//3

(4) The called objectWatcher. expectWeaklyReachable method is a general method to include objects in monitoring. As its name indicates, WeaklyReachable is compared with StronglyReachable. When an object is no longer needed, we want it to change from WeaklyReachable to StronglyReachable.

We can actively call this method when an object is no longer needed to detect memory leaks of any object (except the default object in the previous section):


AppWatcher.objectWatcher.expectWeaklyReachable(obj, "")

In this way, activity is included in the monitoring in onActivityDestroyed callback.

Through the above analysis of Activity into memory leak source code, we can find two key points, first, we need to get the reference of all the objects to be detected in the application, and second, we need an opportunity to end the life cycle of the objects to be detected. These two points can be met at the same time by registering Application. ActivityLifecycleCallbacks interface, but for other class objects, there is no such convenient way.

The following describes how Fragment, ViewModel, RootView, and Service class objects are included in the instrumentation.

FragmentAndViewModelWatcher

Fragment in order to be compatible in Android source code several different package name implementation, they also need to be implemented separately, we only focus on AndroidXFragmentDestroyWatcher AndroidX Fragment memory leak detection in FragmentAndViewModelWatcher, the other several implementation similar.

FragmentAndViewModelWatcher first obtains Activity reference by registering Application. ActivityLifecycleCallbacks callback, and obtains supportFragmentManager of Activity in AndroidXFragmentDestroyWatcher, and registers FragmentManager. FragmentLifecycleCallbacks with it. View of Fragment and Fragment is included in memory leak detection in the onFragmentDestroyed and onFragmentViewDestroyed callbacks.

For the detection of ViewModel, we need to pay attention to ViewModelClearedWatcher, and add spy ViewModel named ViewModelClearedWatcher by using the Activity reference obtained in step 1, so as to obtain the ability to receive onCleared callback, because for one ViewModelStoreOwner (Activity, Fragment), one of its own ViewModel calls back onCleared, then onCleared of other ViewModel should also be called. These ViewModel are obtained by reflection of the mMap attribute of the ViewModelStore. In the onCleared callback of spy ViewModel, memory leak detection is included.

RootViewWatcher

RootView in Window in Android, that is, DecorView, can be detected by registering addOnAttachStateChangeListener when onViewDetachedFromWindow in View. Getting a reference to an object to be detected does not have a callback to rely on like Activity and Fragment1. LeakCanary adopts the way of Hook to replace the container of RootView in install method. Specifically, it modifies the implementation of ArrayList container of mViews in WindowManagerGlobal (including DecorView in all Window) through reflection mechanism, obtains the reference of DecorView in its add method, and then sets OnAttachStateChangeListener callback for detection.

ServiceWatcher

However, Service in Android has no systematic callback to rely on, and LeakCanary adopts Hook to achieve its purpose. Firstly, mServices in ActivityThread is obtained by reflection, which is an Map containing all Service in app. There are two Hook points in the install method. The first is the transit center of Android message mechanism, Handler named H, through which all callbacks from the system side to the application side need to pass. Because the priority executed by mCallback in Handler is greater than that of handleMessage method, Leakcanary replaces mCallback implementation of H. When the message is STOP_SERVICE, Service corresponding to the message is taken out from mServices as Service reference to be detected. The second Hook point is ActivityManagerService, and its serviceDoneExecuting method is modified by dynamic proxy, and memory leak detection is added before it is actually implemented, while other methods remain unchanged.

The timing of inclusion of these classes in testing can be summarized as follows:

如何获取引用 何时纳入监测
Activity ActivityLifecycleCallbacks回调 onActivityDestroyed
Fragment FragmentLifecycleCallbacks回调 onFragmentDestroyed
Fragment中的View FragmentLifecycleCallbacks回调 onFragmentViewDestroyed
ViewModel 反射获取ViewModelStore的mMap spy ViewModel的onCleared
Window中的DecorView Hook WindowManagerGlobal中的mViews onViewDetachedFromWindow
Service Hook H的mCallback实现,当消息为STOP_SERVICE时,从ActivityThread中的mServices获取 Hook ActivityManagerService,serviceDoneExecuting中检测

How to Determine Objects with Memory Leaks

After determining the object to be detected and the timing, look at the expectWeaklyReachable method of ObjectWatcher, and you can know how to select the leaked object from the object to be detected (by default, those class objects with life cycle that we analyzed in the previous section). The principle of determining memory leak objects is our commonly used WeakReference, and its two-parameter constructor supports passing in one ReferenceQueue. When its associated objects are recycled, WeakReference will be added to ReferenceQueue. The method of LeakCanary is to inherit ReferenceQueue, add an attribute key with the value of UUID, and add each object WeakReference to be monitored with this UUID as a key to an map. In this way, after GC, the removeWeaklyReachableObjects method traverses ReferenceQueue and deletes the recycled objects in map through key value, and the remaining objects can basically determine that memory leaks have occurred.

How to Determine the Reference Chain from GC root to the Leaked Object

After the memory leaked object is identified, other means are needed to determine the leaked object reference chain. This process begins with the checkRetainedObjects method, and the trace call can see that the foreground service HeapAnalyzerService is started, which can be seen in the notification bar when we use LeakCanary. The analyze method of HeapAnalyzer is called in the service for heap memory analysis, and the Shark library implements this function, so tracing is no longer carried out.

The above is the analysis of LeakCanary memory leak detection principle details, more about LeakCanary memory leak detection information please pay attention to other related articles on this site!


Related articles: