1. LeakCanary的介绍和使用
1.1 简介
- LeakCanray是Square开源的Java内存泄漏分析工具,用于在开发阶段检测Android应用中常见中的内存泄漏。
- 它支持以下五种Android场景中的内存泄漏监测:
- 已销毁的 Activity 对象(进入 DESTROYED 状态)
- 已销毁的 Fragment 对象和 Fragment View 对象(进入 DESTROYED 状态)
- 已清除的的 ViewModel 对象(进入 CLEARED 状态)
- 已销毁的的 Service 对象(进入 DESTROYED 状态)
- 已从 WindowManager 中移除的 RootView 对象
1.2 使用
- 只需要在 build.gradle 中添加 LeakCanary 依赖即可(LeakCanary内部默认使用了ContentProvider实现无侵入初始化)。注意:LeakCanary 是只在开发环境使用的工具,所以记得使用
debugImplementation
来配置依赖。
// 普通ContentProvider方式
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
// 继承startup的方式,与上面二选一,由于是开发环境,所以意义不大
// debugImplementation 'com.squareup.leakcanary:leakcanary-android-startup:2.12'
// 可选,使用WorkerManager多进程分析堆快照提升分析速度
debugImplementation 'com.squareup.leakcanary:leakcanary-android-process:2.12'
2. 源码分析
2.1 初始化
- 1.x 时期的 LeakCanary 是需要在 Application.onCreate()中加入初始化代码的,2.x之后LeakCanary借助了
ContentProvider
的启动机制来间接调用初始化 API, 实现了无侵入的 LeakCanary 初始化。 - 具体的位置是在 leakcanary-object-watcher-android 这个子module中
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.squareup.leakcanary.objectwatcher"
>
<application>
<!--可以通过配置leak_canary_watcher_auto_install变量设置是否自行初始化,默认为true -->
<provider
android:name="leakcanary.internal.MainProcessAppWatcherInstaller"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false"/>
</application>
internal class MainProcessAppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
// LeakCanary初始化代码
AppWatcher.manualInstall(application)
return true
}
...
}
- 真正的初始化代码为
AppWatcher.manualInstall(application)
// AppWatcher.kt
/** LeakCanary初始化 */
@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), // 默认5s后进行泄漏检测
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
checkMainThread()
...
// 初始化 InternalLeakCanary 内部引擎
LeakCanaryDelegate.loadLeakCanary(application)
// 遍历五种监听器进行分别注册
watchersToInstall.forEach {
it.install()
}
...
}
/** 创建监听集合 */
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
// 对应5中Android泄漏场景(Activity、Fragment和ViewModel、View、Service)
// 传入的reachabilityWatcher均为ReachabilityWatcher的唯一实现类ObjectWatcher
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
- 分为四个监控类分别对Activity、Fragment和ViewModel、RootView以及Service分别监控,接下来分析下这四个类是如何具体监控的
2.2 四大监控类实现五种泄漏场景的监控
2.2.1 ActivityWatcher
- ActivityWatcher主要实现对Activity的监控
/** Activity的泄漏监听 */
class ActivityWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
// 交给objectWatcher分析
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
override fun install() {
// 注册监听的方式
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
- Activity的监听比较简单,直接使用提供的全局监听的Api
application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)
即可,然后在每个Activity的onDestroy中将activity交给objectWatcher去分析即可
2.2.2 FragmentAndViewModelWatcher
- FragmentAndViewModelWatcher主要实现对Fragment、Fragment view以及ViewModel的监控
/**
* 主要负责Fragment的泄漏监听(通过Fragment.onDestroy())
* Fragment View的泄漏监听(通过Fragment.onDestroyView())
* ViewModel的泄漏监听(通过ViewModel.onCleared())
*/
class FragmentAndViewModelWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
/** Fragment监测集合(主要包含几种包下的Fragment) */
// 集合的类型是(Activity)->Unit的函数类型
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
// Android O(8.0)后使用AndroidOFragmentDestroyWatcher监听
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(reachabilityWatcher)
)
}
// androidx包下的Fragment使用AndroidXFragmentDestroyWatcher监听
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
// android.support.v4包下的Fragment使用AndroidSupportFragmentDestroyWatcher监听
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?
) {
// 监听每个Activity的onActivityCreated(),再在其中进行Fragment监听
for (watcher in fragmentDestroyWatchers) {
// 由于fragmentDestroyWatchers里面本身存储的是一个(Activity)->Unit的函数类型
// 所以这里可以直接使用watcher(activity),直接调用,参数为activity,
// 实际执行的是watcher中的invoke()
watcher(activity)
}
}
}
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
...
}
-
Fragment 与 Fragment View 监控 主要是通过
FragmentAndViewModelWatcher
实现,首先是通过Application.registerActivityLifecycleCallbacks(…)
接口监听 Activity.onCreate()事件,再通过FragmentManager.registerFragmentLifecycleCallbacks(…)
接口监听 Fragment 的生命周期 - 这里根据Android版本以及Fragment包不同分了三种状态,分别是
AndroidOFragmentDestroyWatcher
、AndroidXFragmentDestroyWatcher
、AndroidSupportFragmentDestroyWatcher
,这里以AndroidXFragmentDestroyWatcher为例
/** Androidx包下的Fragment、FragmentView以及ViewModel监听 */
internal class AndroidXFragmentDestroyWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : (Activity) -> Unit {
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentCreated(
fm: FragmentManager,
fragment: Fragment,
savedInstanceState: Bundle?
) {
// 注册Fragment级别的ViewModel Hook
ViewModelClearedWatcher.install(fragment, reachabilityWatcher)
}
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
val view = fragment.view
if (view != null) {
// 监听FragmentView.onDestroy()将Fragment.View交给ObjectWatcher分析
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
) {
// 监听Fragment.onDestroy()将Fragment交给ObjectWatcher分析
reachabilityWatcher.expectWeaklyReachable(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}
override fun invoke(activity: Activity) {
// 这段代码会在Activity.onCreate()中执行
if (activity is FragmentActivity) {
val supportFragmentManager = activity.supportFragmentManager
// 注册Fragment生命周期监听
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
// 注册Activity级别的ViewModel Hook
ViewModelClearedWatcher.install(activity, reachabilityWatcher)
}
}
}
- 这里面还包含了ViewModel的监听逻辑,分别在 Activity.onCreate() 和 Fragment.onCreate()中使用
ViewModelClearedWatcher
进行监听,由于系统并未提供ViewModel全局监听的方法,所以 ViewModelClearedWatcher 是通过Hook
方式实现的,我们看下ViewModelClearedWatcher
internal class ViewModelClearedWatcher(
storeOwner: ViewModelStoreOwner,
private val reachabilityWatcher: ReachabilityWatcher
) : ViewModel() {
// 直接通过反射获取ViewModelStore类中的map变量(后面改成mMap),即可获得作用域中的所有ViewModel对象
private val viewModelMap: Map<String, ViewModel>? = try {
val storeClass = ViewModelStore::class.java
val mapField = try {
storeClass.getDeclaredField("map")
} catch (exception: NoSuchFieldException) {
storeClass.getDeclaredField("mMap")
}
mapField.isAccessible = true
@Suppress("UNCHECKED_CAST")
mapField[storeOwner.viewModelStore] as Map<String, ViewModel>
} catch (ignored: Exception) {
SharkLog.d(ignored) { "Could not find ViewModelStore map of view models" }
null
}
override fun onCleared() {
// 遍历当前作用域所有ViewModel对象
viewModelMap?.values?.forEach { viewModel ->
// 使用ObjectWatcher.expectWeaklyReachable
reachabilityWatcher.expectWeaklyReachable(
viewModel, "${viewModel::class.java.name} received ViewModel#onCleared() callback"
)
}
}
companion object {
// 在storeOwner作用域实例化ViewModelClearedWatcher对象
fun install(
storeOwner: ViewModelStoreOwner,
reachabilityWatcher: ReachabilityWatcher
) {
val provider = ViewModelProvider(storeOwner, object : Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
// 直接在storeOwner作用域实例化ViewModelClearedWatcher对象
ViewModelClearedWatcher(storeOwner, reachabilityWatcher) as T
})
provider.get(ViewModelClearedWatcher::class.java)
}
}
}
- 由此可见:ViewModel的监测是在Activity.onCreate()/Fragment.onCreate()中分别创建一个自定义的ViewModel(即ViewModelClearedWatcher),然后监听ViewModelClearedWatcher的onCleared(),通过Hook ViewModelStore的方式获取相应应作用域(Activity/Fragment)中的所有ViewModel对象,并最终交给ObjectWatcher进行监控
2.2.3 RootViewWatcher
- RootViewWatcher是处理RootView内存泄漏的监听器,由于系统并未提供全局RootView的监听,所以LeakCannary还是通过Hook方式来处理的,只不过这里没有自行Hook而是利用了square的另一个开源库curtains来处理的,主要通过Hook WMS 服务内部的
WindowManagerGlobal
(sdk>16)或者WindowManagerImpl
(sdk<=16)来获取所有的RootView新增或移除的时机
/**
* RootView泄漏监听,主要利用了Curtains库实现对
*/
class RootViewWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val listener = OnRootViewAddedListener { rootView ->
// 判断rootView的窗口类型
// 是否需要使用RootViewWatcher监听
val trackDetached = when(rootView.windowType) {
PHONE_WINDOW -> {
when (rootView.phoneWindow?.callback?.wrappedCallback) {
// Activities are already tracked by ActivityWatcher
// 由于Activity已经有ActivityWatcher监听,这里直接返回false,即无需通过RootViewWatcher监听
is Activity -> false
is Dialog -> {
// Use app context resources to avoid NotFoundException
// https://github.com/square/leakcanary/issues/2137
// 通过配置开启,默认不开启
val resources = rootView.context.applicationContext.resources
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等进行监听
TOOLTIP, TOAST, UNKNOWN -> true
}
if (trackDetached) {
// 注册监听事件
rootView.addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
val watchDetachedView = Runnable {
// 接收发送的消息,使用ObjectWatcher对rootView进行监听
reachabilityWatcher.expectWeaklyReachable(
rootView, "${rootView::class.java.name} received View#onDetachedFromWindow() callback"
)
}
override fun onViewAttachedToWindow(v: View) {
// 添加时移除消息
mainHandler.removeCallbacks(watchDetachedView)
}
override fun onViewDetachedFromWindow(v: View) {
// 监听RootView的移除事件,使用Handler发送消息处理
mainHandler.post(watchDetachedView)
}
})
}
}
override fun install() {
// 注册RootView监听
Curtains.onRootViewsChangedListeners += listener
}
override fun uninstall() {
Curtains.onRootViewsChangedListeners -= listener
}
}
- 主要是针对
Dialog(有监听开关配置)、DreamService、Tooltip、Toast
等类型的RootView进行监听,通过调用rootView.addOnAttachStateChangeListener
监听onViewDetachedFromWindow
方法,监听RootView的移除事件,在移除事件中将RootView交给ObjectWatcher进行监控
2.2.4 ServiceWatcher
- ServiceWatcher是处理Service内存泄漏的监听器,系统也并未提供全局Service的移除监听,所以LeaakCanary也是通过Hook方式实现的
@SuppressLint("PrivateApi")
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
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<IBinder, Service>
}
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 {
// Hook ActivityThread类中的mH.mCallback
swapActivityThreadHandlerCallback { mCallback ->
uninstallActivityThreadHandlerCallback = {
swapActivityThreadHandlerCallback {
mCallback
}
}
Handler.Callback { msg ->
if (msg.obj !is IBinder) {
return@Callback false
}
// 监听Service.onStop()事件消息
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
// activityThreadServices是通过反射获取的ActivityThread类中的mServices成员变量<IBinder, Service>
activityThreadServices[key]?.let {
// 服务销毁前的处理,这里主要是暂存
onServicePreDestroy(key, it)
}
}
// Hook后继续执行Framework本身的逻辑
mCallback?.handleMessage(msg) ?: false
}
}
// Hook AMS IActivityManager
swapActivityManager { activityManagerInterface, activityManagerInstance ->
uninstallActivityManager = {
swapActivityManager { _, _ ->
activityManagerInstance
}
}
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) { _, method, args ->
// 代理serviceDoneExecuting()方法
if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token)) {
// 处理Service销毁,主要是将service交给ObjectWatcher进行监控
onServiceDestroyed(token)
}
}
// 继续执行serviceDoneExecuting()本身的方法
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"
)
}
}
}
/**
* Hook修改ActivityThread类中的mH.mCallback
* swap 是一个 lambda 表达式,参数为原对象,返回值为注入的新对象
*/
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)
}
/**
* Hook修改ActivityThread类中的mH.mCallback
* swap 是一个 lambda 表达式,参数为 IActivityManager 的 Class 对象和接口原实现对象,返回值为注入的新对象
*/
@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]
val activityManagerInstance = singletonGetMethod.invoke(activityManagerSingletonInstance)
val iActivityManagerInterface = Class.forName("android.app.IActivityManager")
// 将swap的返回值作为新对象,实现 Hook
mInstanceField[activityManagerSingletonInstance] =
swap(iActivityManagerInterface, activityManagerInstance!!)
}
companion object {
private const val STOP_SERVICE = 116
private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
}
}
- Hook的大致步骤为:
- Hook主线程消息循环的
mH.mCallback
回调,监听其中的STOP_SERVICE
消息,将即将 Destroy 的 Service 对象暂存起来(由于 ActivityThread.H 中没有 DESTROY_SERVICE 消息,所以不能直接监听到 onDestroy() 事件,需要下面的步骤); - 使用动态代理 Hook AMS 与 App 通信的的
IActivityManager
Binder 对象,代理其中的serviceDoneExecuting()
方法,视为 Service.onDestroy() 的执行时机,拿到暂存的 Service 对象交给 ObjectWatcher 监控。
- Hook主线程消息循环的
2.3 ObjectWatcher如何监控内存泄漏
2.3.1 Java四大引用相关知识
-
强引用
- 普通的最常见的引用,当对象具有强引用且未被置空,虚拟机即使OOM也不会回收
-
软引用
- 当内存充足时虚拟机不会回收它,内存不足时会自动回收,常用于图片缓存
SoftReference<String> softReference = new SoftReference<>(str);
-
弱引用
- 无论内存是否充足,只要手动调用垃圾回收System.gc()或等待虚拟机自动GC,弱引用就会被回收。主要用来放置在容易引起内存泄漏的位置,如Android中的Handler
WeakReference<String> weakReference = new WeakReference<>(str);
-
虚引用
- 虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。主要用来跟踪对象被垃圾回收器回收的活动,使用 PhantomReference 来创建
-
引用队列ReferenceQueue
- 当gc准备回收一个对象时,如果发现它还仅有软引用(或弱引用,或虚引用)指向它,就会在回收该对象之前,把这个软引用(或弱引用,或虚引用)加入到与之关联的引用队列(ReferenceQueue)中。如果一个软引用(或弱引用,或虚引用)对象本身在引用队列中,就说明该引用对象所指向的对象被回收了;
// 构造一个强引用
Object obj = new Object();
// 创建引用队列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
// 利用强引用和引用队列构造弱引用
WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue);
System.out.println("调用GC前弱引用:" + weakReference.get()); // java.lang.Object@6108b2d7
System.out.println("调用GC前引用队列:" + referenceQueue.poll()); // null
// 将强引用手动置null
obj = null;
// 手动GC或虚拟机自动GC都会回收弱引用,这里手动调用GC
System.gc();
System.out.println("调用GC后弱引用:" + weakReference.get()); // null
System.out.println("调用GC后引用队列:" + referenceQueue.poll()); // java.lang.Object@6108b2d7
2.3.2 ObjectWatcher对象监控
class ObjectWatcher constructor(
private val clock: Clock,
private val checkRetainedExecutor: Executor,
private val isEnabled: () -> Boolean = { true }
) : ReachabilityWatcher {
private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()
/** 被监控对象的映射表 */
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
/** 弱引用引用队列,与KeyedWeakReference相关联,对象正常销毁会存在这里面 */
private val queue = ReferenceQueue<Any>()
...
/** 监控对象泄漏 */
@Synchronized
override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
// 移除watchedObjects中未泄漏的对象
removeWeaklyReachableObjects()
// 构造KeyedWeakReference 引用对象
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
// 默认5秒后执行检查
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
...
@Synchronized
private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
// 移除watchedObjects中未泄露的对象后剩余的判定为发生泄漏
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
// 回调通知LeakCanary内部处理
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
/** 移除队列中未泄漏的对象 */
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
}
/** 弱引用包装类 */
class KeyedWeakReference(
/** 被监控对象 */
referent: Any,
/** 映射表的Key */
val key: String,
/** 描述 */
val description: String,
/** 监控开始时间(引用创建时间) */
val watchUptimeMillis: Long,
/** 关联的引用队列 */
referenceQueue: ReferenceQueue<Any>
) : WeakReference<Any>(
referent, referenceQueue
) {
/** 判定对象为泄漏对象的时间,-1表示非泄漏对象或还未判定完毕 */
@Volatile
var retainedUptimeMillis = -1L
override fun clear() {
super.clear()
retainedUptimeMillis = -1L
}
companion object {
/** 记录最近一次触发Heap Dump的时间 */
@Volatile
@JvmStatic var heapDumpUptimeMillis = 0L
}
}
- 主要经历了三个步骤
- 为被监控对象
watchedObject
创建一个KeyedWeakReference
弱引用,并存储到 <UUID, KeyedWeakReference> 的映射表中; - postDelay 五秒后检查引用对象是否出现在引用队列中,出现在队列则说明被监控对象未发生泄漏。随后,移除映射表中未泄露的记录,更新泄漏的引用对象的
retainedUptimeMillis
字段以标记为泄漏 - 通过回调
onObjectRetained
告知 LeakCanary 内部发生新的内存泄漏
2.4 Dump heap获取内存快照文件
- ObjectWatcher 判定被监控对象发生泄漏后,会通过接口方法
OnObjectRetainedListener.onObjectRetained()
回调到 LeakCanary 内部的管理器InternalLeakCanary
- 由于分析工作比较耗时间,LeakCanary自然不会每次发现内存泄漏对象都进行分析工作,而会进行两个拦截:
- 1. 泄漏对象计数未达到阈值,或者进入后台时间未达到阈值
- 2. 计算距离上一次 HeapDump 未超过 60S
// InternalLeakCanary.kt
override fun onObjectRetained() = scheduleRetainedObjectCheck()
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
// HeapDumpTrigger.kt
fun scheduleRetainedObjectCheck(
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
// 避免重复postDelayed
if (checkCurrentlyScheduledAt > 0) {
return
}
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects()
}, 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.retainedObjectCount
// 泄漏计数>0时主动调用GC
if (retainedReferenceCount > 0) {
// 这个方法是调用RunTime.getRuntime().GC()并休眠等待100ms
gcTrigger.runGc()
// GC后再获取泄漏计数
retainedReferenceCount = objectWatcher.retainedObjectCount
}
val nopeReason = iCanHasHeap.reason()
// 这里会判断泄漏计数是否>5(默认阈值)
val wouldDump = !checkRetainedCount(
retainedReferenceCount, config.retainedVisibleThreshold, nopeReason
)
if (wouldDump) {
val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)
// 回调onEvent
onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))
// 展示通知
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = uppercaseReason
)
}
} else {
SharkLog.d {
application.getString(
R.string.leak_canary_heap_dump_disabled_text, iCanHasHeap.reason()
)
}
}
return
}
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
// 计算距离上一次HeapDump时间未超过60s会拦截
if (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分析
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
2.5 分析堆快照dumpHeap
- LeakCanary 已经成功生成
.hprof
堆快照文件,并且发送了一个 LeakCanary 内部事件HeapDump
。LeakCanary 的配置项中设置了多个事件消费者EventListener,这三个会根据 App 当前的依赖项而选择最优的执行策略: - 1 - WorkerManager 多进程分析
- 2 - WorkManager 异步分析
- 3 - 异步线程分析(兜底策略)
// LeakCanary.kt
val eventListeners: List<EventListener> = listOf(
LogcatEventListener,
ToastEventListener,
LazyForwardingEventListener {
if (InternalLeakCanary.formFactor == TV) TvEventListener else NotificationEventListener
},
when {
// WorkManager多进程分析
RemoteWorkManagerHeapAnalyzer.remoteLeakCanaryServiceInClasspath ->
RemoteWorkManagerHeapAnalyzer
// WorkManager 异步分析
WorkManagerHeapAnalyzer.validWorkManagerInClasspath -> WorkManagerHeapAnalyzer
// 异步线程分析(兜底策略)
else -> BackgroundThreadHeapAnalyzer
}
),
2.5.1 WorkerManager 多进程分析
object RemoteWorkManagerHeapAnalyzer : EventListener {
private const val REMOTE_SERVICE_CLASS_NAME = "leakcanary.internal.RemoteLeakCanaryWorkerService"
// 这里通过RemoteLeakCanaryWorkerService这个类是否加载成功来判断
// 是否有'com.squareup.leakcanary:leakcanary-android-process:2.9.1'这个依赖
internal val remoteLeakCanaryServiceInClasspath by lazy {
try {
Class.forName(REMOTE_SERVICE_CLASS_NAME)
true
} catch (ignored: Throwable) {
false
}
}
override fun onEvent(event: Event) {
if (event is HeapDump) {
val application = InternalLeakCanary.application
// 创建并分发 WorkManager 多进程请求
val heapAnalysisRequest =
OneTimeWorkRequest.Builder(RemoteHeapAnalyzerWorker::class.java).apply {
val dataBuilder = Data.Builder()
.putString(ARGUMENT_PACKAGE_NAME, application.packageName)
.putString(ARGUMENT_CLASS_NAME, REMOTE_SERVICE_CLASS_NAME)
setInputData(event.asWorkerInputData(dataBuilder))
with(WorkManagerHeapAnalyzer) {
addExpeditedFlag()
}
}.build()
SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
val workManager = WorkManager.getInstance(application)
workManager.enqueue(heapAnalysisRequest)
}
}
}
internal class RemoteHeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
RemoteListenableWorker(appContext, workerParams) {
override fun startRemoteWork(): ListenableFuture<Result> {
val heapDump = inputData.asEvent<HeapDump>()
val result = SettableFuture.create<Result>()
heapAnalyzerThreadHandler.post {
// 分析堆快照
val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(heapDump, isCanceled = {
result.isCancelled
}) { progressEvent ->
if (!result.isCancelled) {
// 发送分析进度事件
InternalLeakCanary.sendEvent(progressEvent)
}
}
if (result.isCancelled) {
SharkLog.d { "Remote heap analysis for ${heapDump.file} was canceled" }
} else {
// 发送分析完成事件
InternalLeakCanary.sendEvent(doneEvent)
result.set(Result.success())
}
}
return result
}
override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
return applicationContext.heapAnalysisForegroundInfoAsync()
}
}
2.5.2 WorkManager异步分析
object WorkManagerHeapAnalyzer : EventListener {
// 判断是否包含WorkManager
internal val validWorkManagerInClasspath by lazy {
try {
Class.forName("androidx.work.WorkManager")
val dataBuilderClass = Class.forName("androidx.work.Data$Builder")
dataBuilderClass.declaredMethods.any { it.name == "putByteArray" }.apply {
if (!this) {
SharkLog.d { "Could not find androidx.work.Data$Builder.putByteArray, WorkManager should be at least 2.1.0." }
}
}
} catch (ignored: Throwable) {
false
}
}
// setExpedited() requires WorkManager 2.7.0+
private val workManagerSupportsExpeditedRequests by lazy {
try {
Class.forName("androidx.work.OutOfQuotaPolicy")
true
} catch (ignored: Throwable) {
false
}
}
internal fun OneTimeWorkRequest.Builder.addExpeditedFlag() = apply {
if (workManagerSupportsExpeditedRequests) {
setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
}
}
override fun onEvent(event: Event) {
if (event is HeapDump) {
val heapAnalysisRequest = OneTimeWorkRequest.Builder(HeapAnalyzerWorker::class.java).apply {
setInputData(event.asWorkerInputData())
addExpeditedFlag()
}.build()
SharkLog.d { "Enqueuing heap analysis for ${event.file} on WorkManager remote worker" }
val application = InternalLeakCanary.application
WorkManager.getInstance(application).enqueue(heapAnalysisRequest)
}
}
}
internal class HeapAnalyzerWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
// 分析堆快照
val doneEvent =
AndroidDebugHeapAnalyzer.runAnalysisBlocking(inputData.asEvent()) { event ->
// 发送分析进度事件
InternalLeakCanary.sendEvent(event)
}
// 发送分析完成事件
InternalLeakCanary.sendEvent(doneEvent)
return Result.success()
}
...
}
2.5.3 普通异步线程分析BackgroundThreadHeapAnalyzer
object BackgroundThreadHeapAnalyzer : EventListener {
internal val heapAnalyzerThreadHandler by lazy {
val handlerThread = HandlerThread("HeapAnalyzer")
handlerThread.start()
Handler(handlerThread.looper)
}
override fun onEvent(event: Event) {
if (event is HeapDump) {
heapAnalyzerThreadHandler.post {
// 分析堆快照
val doneEvent = AndroidDebugHeapAnalyzer.runAnalysisBlocking(event) { event ->
// 发送分析进度事件
InternalLeakCanary.sendEvent(event)
}
// 发送分析完成事件
InternalLeakCanary.sendEvent(doneEvent)
}
}
}
}
- 可以看到三种方式中最终LeakCanary 是通过子线程或者子进程执行
AndroidDebugHeapAnalyzer.runAnalysisBlocking
方法来分析堆快照的,并在分析过程中和分析完成后发送回调事件。 - 堆快照分析最终是交给 Shark 中的 HeapAnalizer 完成的,这里就不再展开分析了,核心流程是:
- 1、在堆快照中寻找泄漏对象,默认是寻找 KeyedWeakReference 类型对象;
- 2、分析 KeyedWeakReference 对象的最短引用链,并按照引用链签名分组,按照 Application Leaks 和 Library Leaks 分类;
- 3、返回分析完成事件。
3. 总结
- 利用ContentProvider进行初始化
- 注册 5 种 Android 泄漏场景的监控
- Activity是利用application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)监听全局Activity生命周期
- Fragment首先是通过
Application.registerActivityLifecycleCallbacks(…)
接口监听 Activity.onCreate()事件,再通过FragmentManager.registerFragmentLifecycleCallbacks(…)
接口监听 Fragment 的生命周期 - ViewModel是通过
Hook
ViewModelStore的方式实现的 - Service是通过Hook
mH.mCallback
回调,监听其中的STOP_SERVICE
消息来实现的 - RootView是通过借助curtains库Hook WMS 服务内部的
WindowManagerGlobal
实现
- 收到销毁回调后,根据要回收对象创建 KeyedWeakReference 并关联 ReferenceQueue,延迟 5 秒检查相关对象是否被回收,如果未被回收则开启服务,dump heap 获取内存快照
.hprof
文件 - 通过 Shark 库解析
.hprof
文件,获取泄漏对象,计算泄漏对象到 GC roots 的最短路径 - 合并多个泄漏路径并输出分析结果,将结果展示到可视化界面