注册

LeakCanary原理分析

LeakCanary 是一个很好用的Android内存泄露检测工具,今天从源码角度分析下其检测内存泄露的原理,不同版本 源码 会有一定差异,这里参考的是2.7版本。

1. Reference简介

Java中的四种引用类型,我们先简单复习下

  • 强引用,对象有强引用时不能被回收
  • 软引用 SoftReference,对象只有软引用时,在内存不足时触发GC会回收该对象
  • 弱引用 WeakReference,对象只有弱引用时,下次GC就会回收该对象
  • 虚引用 PhantomReference,平常很少会用到,源码注释主要用来监听对象清理前的动作,比Java finalization更灵活,PhantomReference需要与 ReferenceQueue 一起配合使用。

Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.

ReferenceQueue

上面提到PhantomReferenceReferenceQueue配合监听对象被回收,实际上WeakReferenceSoftReference同样可以与ReferenceQueue关联使用,只要构造方法传入ReferenceQueue参数即可。在引用所指的对象被回收后,引用本身将会被加入到ReferenceQueue之中。

2. LeakCanary使用简介

  • 在app的build.gradle中加入依赖
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
  • LeakCanary会自动监控Activity、Fragment、Fragment View、RootView、Service的泄露
  • 如果需要监控其它对象的泄露,可以手动添加如下代码
AppWatcher.objectWatcher.watch(myView, "View was detached")

3. LeakCanary源码分析

初始化

LeakCanary新版本使用ContentProvider自动初始化,不需要再手动调用install方法


    android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false" />

如果想禁用自动初始化,在app res中加入

false

接下来我们从源码分析下LeakCanary初始化的流程:

internal sealed class AppWatcherInstaller : ContentProvider() {

/**
* [MainProcess] automatically sets up the LeakCanary code that runs in the main app process.
*/
internal class MainProcess : AppWatcherInstaller()

/**
* When using the `leakcanary-android-process` artifact instead of `leakcanary-android`,
* [LeakCanaryProcess] automatically sets up the LeakCanary code
*/
internal class LeakCanaryProcess : AppWatcherInstaller()

override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}

...
}

AppWatcherInstaller继承ContentProvider,onCreate会调用AppWatcher的manualInstall方法,完成自动初始化。

object AppWatcher {

/**
* The [ObjectWatcher] used by AppWatcher to detect retained objects.
* Only set when [isInstalled] is true.
*/
val objectWatcher = ObjectWatcher(
clock = { SystemClock.uptimeMillis() },
checkRetainedExecutor = {
check(isInstalled) {
"AppWatcher not installed"
}
mainHandler.postDelayed(it, retainedDelayMillis)
},
isEnabled = { true }
)

@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List = appDefaultWatchers(application)
) {
checkMainThread()
if (isInstalled) {
throw IllegalStateException(
"AppWatcher already installed, see exception cause for prior install call", installCause
)
}
check(retainedDelayMillis >= 0) {
"retainedDelayMillis $retainedDelayMillis must be at least 0 ms"
}
installCause = RuntimeException("manualInstall() first called here")
this.retainedDelayMillis = retainedDelayMillis
if (application.isDebuggableBuild) {
LogcatSharkLog.install()
}
// Requires AppWatcher.objectWatcher to be set
LeakCanaryDelegate.loadLeakCanary(application)

watchersToInstall.forEach {
it.install()
}
}

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

manualInstall方法有3个参数:

  • application:application对象
  • retainedDelayMillis:默认值5s,表示5s后检测对象是否被回收
  • watchersToInstall:安装的监控器,每个监控器抽象成InstallableWatcher,默认值在appDefaultWatchers方法中定义,包括ActivityWatcher、FragmentAndViewModelWatcher、RootViewWatcher、ServiceWatcher,后面单独分析。

创建InstallableWatcher时需要传入ReachabilityWatcher,实现类是ObjectWatcher,这是监控对象可达性的核心。AppWatcher创建了ObjectWatcher对象,并在checkRetainedExecutor里面加入了延迟5s的逻辑,下面配合ObjectWatcher源码一起分析。

ObjectWatcher

上面说到手动监控其它对象是调用ObjectWatcher的watch方法,这里是真正的核心逻辑,我们看下其部分代码

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 {

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] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}

@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)
}

}

调用watch方法会走到expectWeaklyReachable,里面大致做了4件事情:

  • 从watchedObjects中移除已经正常释放的引用 removeWeaklyReachableObjects()。这里用到了前面讲的ReferenceQueue,对象被回收会加入到queue中,将queu中存在的从watchedObjects中移除。
  • 创建KeyedWeakReference引用,KeyedWeakReference继承WeakReference,创建时传入了ReferenceQueue,监听对象的回收。
  • 将引用加入到watchedObjects中 watchedObjects[key] = reference
  • 延迟5s之后执行moveToRetained(key),确认对象是否回收(延迟逻辑在AppWatcher传入的checkRetainedExecutor中实现)。moveToRetained中首先执行removeWeaklyReachableObjects,之后再判断watchedObjects中是否还存在此key,若存在说明对象未被回收,发生内存泄露。

到这里我们基本明白了LeakCanary监控对象是否回收的逻辑,接下来我们再看看他是如何自动监控Activity、Fragment等组件的。前面讲到AppWatcher初始化时,会自动创建ActivityWatcher、FragmentAndViewModelWatcher、RootViewWatcher、ServiceWatcher,我们阅读下他们的源码。

ActivityWatcher

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"
)
}
}

override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}

override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}

ActivityWatcher的代码非常简单,注册Activity生命周期回调,在onActivityDestroyed中调用ObjectWatcher的expectWeaklyReachable,监控activity对象5s之内是否被释放。

FragmentAndViewModelWatcher

FragmentAndViewModelWatcher监控Fragment和Fragment View的泄露,原理与Activity类似,在onFragmentDestroyed和onFragmentViewDestroyed中调用ObjectWatcher的expectWeaklyReachable方法。只不过监听Fragment的onDestroy相对复杂点,原理是先监听Activity生命周期,然后在Activity onCreate时通过fragmentManager.registerFragmentLifecycleCallbacks注册Fragment生命周期回调。而且同时兼容了android.app.Fragment、androidx.fragment.app.Fragment、android.support.v4.app.Fragment。

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

@SuppressLint("NewApi")
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.view
if (view != null) {
reachabilityWatcher.expectWeaklyReachable(
view, "${fragment::class.java.name} received Fragment#onDestroyView() callback " +
"(references to its views should be cleared to prevent leaks)"
)
}
}

override fun onFragmentDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
reachabilityWatcher.expectWeaklyReachable(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}

override fun invoke(activity: Activity) {
val fragmentManager = activity.fragmentManager
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
}
}

RootViewWatcher

RootViewWatcher监控RootView的泄露,在rootView onDetachedFromWindow回调时,调用ObjectWatcher的expectWeaklyReachable方法。rootView的onDetachedFromWindow回调监听是通过Square开源的Curtains库实现。

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 ActivityWatcher
is Activity -> false
is Dialog -> rootView.resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
// Probably a DreamService
else -> true
}
}
// Android widgets keep detached popup window instances around.
POPUP_WINDOW -> false
TOOLTIP, TOAST, UNKNOWN -> true
}
if (trackDetached) {
rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {

val watchDetachedView = Runnable {
reachabilityWatcher.expectWeaklyReachable(
rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
)
}

override fun onViewAttachedToWindow(v: View) {
mainHandler.removeCallbacks(watchDetachedView)
}

override fun onViewDetachedFromWindow(v: View) {
mainHandler.post(watchDetachedView)
}
})
}
}

override fun install() {
Curtains.onRootViewsChangedListeners += listener
}

override fun uninstall() {
Curtains.onRootViewsChangedListeners -= listener
}
}

ServiceWatcher

ServiceWatcher监控Service的泄露,原理也是在Service的onDestroy时调用ObjectWatcher的expectWeaklyReachable方法。Service的onDestroy是通过反射和动态代理ActivityManager和ActivityThread,代码比较巧妙,可以仔细消化下。

@SuppressLint("PrivateApi")
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {

private val servicesToBeDestroyed = WeakHashMap>()

private val activityThreadClass by lazy { Class.forName("android.app.ActivityThread") }

private val activityThreadInstance by lazy {
activityThreadClass.getDeclaredMethod("currentActivityThread").invoke(null)!!
}

private val activityThreadServices by lazy {
val mServicesField =
activityThreadClass.getDeclaredField("mServices").apply { isAccessible = true }

@Suppress("UNCHECKED_CAST")
mServicesField[activityThreadInstance] as Map
}

private var uninstallActivityThreadHandlerCallback: (() -> Unit)? = null
private var uninstallActivityManager: (() -> Unit)? = null

override fun install() {
checkMainThread()
check(uninstallActivityThreadHandlerCallback == null) {
"ServiceWatcher already installed"
}
check(uninstallActivityManager == null) {
"ServiceWatcher already installed"
}
try {
swapActivityThreadHandlerCallback { mCallback ->
uninstallActivityThreadHandlerCallback = {
swapActivityThreadHandlerCallback {
mCallback
}
}
Handler.Callback { msg ->
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
activityThreadServices[key]?.let {
onServicePreDestroy(key, it)
}
}
mCallback?.handleMessage(msg) ?: false
}
}
swapActivityManager { activityManagerInterface, activityManagerInstance ->
uninstallActivityManager = {
swapActivityManager { _, _ ->
activityManagerInstance
}
}
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) { _, method, args ->
if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token)) {
onServiceDestroyed(token)
}
}
try {
if (args == null) {
method.invoke(activityManagerInstance)
} else {
method.invoke(activityManagerInstance, *args)
}
} catch (invocationException: InvocationTargetException) {
throw invocationException.targetException
}
}
}
} catch (ignored: Throwable) {
SharkLog.d(ignored) { "Could not watch destroyed services" }
}
}

override fun uninstall() {
checkMainThread()
uninstallActivityManager?.invoke()
uninstallActivityThreadHandlerCallback?.invoke()
uninstallActivityManager = null
uninstallActivityThreadHandlerCallback = null
}

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"
)
}
}
}

private fun swapActivityThreadHandlerCallback(swap: (Handler.Callback?) -> Handler.Callback?) {
val mHField =
activityThreadClass.getDeclaredField("mH").apply { isAccessible = true }
val mH = mHField[activityThreadInstance] as Handler

val mCallbackField =
Handler::class.java.getDeclaredField("mCallback").apply { isAccessible = true }
val mCallback = mCallbackField[mH] as Handler.Callback?
mCallbackField[mH] = swap(mCallback)
}

@SuppressLint("PrivateApi")
private fun swapActivityManager(swap: (Class<*>, Any) -> Any) {
val singletonClass = Class.forName("android.util.Singleton")
val mInstanceField =
singletonClass.getDeclaredField("mInstance").apply { isAccessible = true }

val singletonGetMethod = singletonClass.getDeclaredMethod("get")

val (className, fieldName) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
"android.app.ActivityManager" to "IActivityManagerSingleton"
} else {
"android.app.ActivityManagerNative" to "gDefault"
}

val activityManagerClass = Class.forName(className)
val activityManagerSingletonField =
activityManagerClass.getDeclaredField(fieldName).apply { isAccessible = true }
val activityManagerSingletonInstance = activityManagerSingletonField[activityManagerClass]

// Calling get() instead of reading from the field directly to ensure the singleton is
// created.
val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)

val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
mInstanceField[activityManagerSingletonInstance] =
swap(iActivityManagerInterface, activityManagerInstance!!)
}

companion object {
private const val STOP_SERVICE = 116

private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
}
}

总结

今天从源码角度分析了LeakCanary监控内存泄露的核心原理。除此之外,LeakCanary还可以导出、分析、分类堆栈,如果后面有时间咱们再单独讲吧。

0 个评论

要回复文章请先登录注册