1. Introducción:
LeakCanray es la herramienta de análisis de fugas de memoria Java de código abierto de Square, que se utiliza para detectar fugas de memoria comunes en aplicaciones de Android durante la fase de desarrollo.
- Admite la supervisión de pérdidas de memoria en los siguientes cinco escenarios de Android:
- Objeto de actividad destruido (entra en estado DESTRUIDO)
- Objetos Fragmento destruidos y objetos Vista Fragmento (entraron en estado DESTRUIDO)
- Objeto ViewModel borrado (entra en estado BORRADO)
- Objeto de servicio destruido (entra en estado DESTRUIDO)
- Objeto RootView eliminado de WindowManager
1.2 Uso
- Simplemente agregue la dependencia de LeakCanary en build.gradle (LeakCanary usa ContentProvider de forma predeterminada para lograr una inicialización no intrusiva). Nota: LeakCanary es una herramienta que solo se usa en el entorno de desarrollo, así que recuerde usarla
debugImplementation
para configurar dependencias.
// 普通ContentProvider方式
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
// 可选,使用WorkerManager多进程分析堆快照提升分析速度
debugImplementation 'com.squareup.leakcanary:leakcanary-android-process:2.12'
2. Análisis del código fuente
2.1 Inicialización
- LeakCanary en la era 1.x requería agregar código de inicialización a Application.onCreate(). Después de 2.x, LeakCanary usó el
ContentProvider
mecanismo de inicio para llamar indirectamente a la API de inicialización, logrando una inicialización de LeakCanary no invasiva. - La ubicación específica está en el submódulo fugascanary-object-watcher-android
<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
}
...
}
El código de inicialización real esAppWatcher.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)
)
}
Se divide en cuatro categorías de monitoreo para monitorear Actividad, Fragmento, ViewModel, RootView y Servicio respectivamente. A continuación, analicemos cómo se monitorean específicamente estas cuatro categorías.
2.2 Cuatro categorías principales de monitoreo realizan el monitoreo de cinco escenarios de fugas
2.2.1 Vigilante de actividad
- ActivityWatcher realiza principalmente el seguimiento de la actividad
/** 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)
}
}
- El monitoreo de actividad es relativamente simple: puede usar directamente la API de monitoreo global proporcionada
application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback)
y luego entregar la actividad a objectWatcher para su análisis en onDestroy de cada actividad.
2.2.2 FragmentAndViewModelWatcher
- FragmentAndViewModelWatcher implementa principalmente el monitoreo de Fragment, Fragment view y 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)
}
...
}
- El monitoreo de Fragment y Fragment View se implementa principalmente
FragmentAndViewModelWatcher
a través de: Primero,Application.registerActivityLifecycleCallbacks(…)
monitorea el evento Activity.onCreate () a través de la interfaz y luegoFragmentManager.registerFragmentLifecycleCallbacks(…)
monitorea el ciclo de vida de Fragment a través de la interfaz. - Hay tres estados según la versión de Android y el paquete Fragment, a saber
AndroidOFragmentDestroyWatcher
,,, Aquí tomamos AndroidXFragmentDestroyWatcher como ejemplo.AndroidXFragmentDestroyWatcher
AndroidSupportFragmentDestroyWatcher
/** 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)
}
}
}
- Esto también incluye la lógica de monitoreo de ViewModel, que se usa en Activity.onCreate() y Fragment.onCreate()
ViewModelClearedWatcher
para el monitoreo. Dado que el sistema no proporciona un método para el monitoreo global de ViewModel, ViewModelClearedWatcher seHook
implementa a través de métodos. Tomemos un mirar.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)
}
}
}
- Se puede ver que el monitoreo de ViewModel consiste en crear un ViewModel personalizado (es decir, ViewModelClearedWatcher) en Activity.onCreate()/Fragment.onCreate(), luego escuchar onCleared() de ViewModelClearedWatcher y obtener la respuesta correspondiente a través de Hook ViewModelStore. Todos los objetos ViewModel en el dominio (Actividad/Fragmento) y finalmente entregados a ObjectWatcher para su monitoreo.
2.2.3 Vigilante RootView
- RootViewWatcher es un oyente que maneja las pérdidas de memoria de RootView. Dado que el sistema no proporciona monitoreo global de RootView, LeakCannary aún se maneja a través de Hook. Sin embargo, en lugar de engancharse a sí mismo, se usa Curtains, otra biblioteca de código abierto de Square, para manejarlo. principalmente a través del servicio Hook WMS interno
WindowManagerGlobal
(sdk>16) oWindowManagerImpl
(sdk<=16) para obtener el momento de agregar o eliminar todos los RootViews
/**
* 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
}
}
- Principalmente para
Dialog(有监听开关配置)、DreamService、Tooltip、Toast
monitorear otros tipos de RootView, llamando al métodorootView.addOnAttachStateChangeListener
de monitoreoonViewDetachedFromWindow
, monitoreando el evento de eliminación de RootView y entregando RootView a ObjectWatcher para monitorear el evento de eliminación.
2.2.4 Vigilante del servicio
- ServiceWatcher es un oyente que maneja las pérdidas de memoria del Servicio. El sistema no proporciona monitoreo global de eliminación del Servicio, por lo que LeaakCanary también se implementa a través de 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"
}
}
- Los pasos generales de Hook son:
- Enganche la devolución de llamada del bucle de mensajes del hilo principal
mH.mCallback
, monitoreeSTOP_SERVICE
los mensajes y almacene temporalmente el objeto de Servicio que está a punto de ser destruido (dado que no hay ningún mensaje DESTROY_SERVICE en ActivityThread.H, el evento onDestroy() no se puede monitorear directamente y el se requieren los siguientes pasos); - Utilice el proxy dinámico Hook AMS para comunicarse con el objeto Binder de la aplicación
IActivityManager
y proxy los métodos que contieneserviceDoneExecuting()
. Esto se considera el tiempo de ejecución de Service.onDestroy (), y el objeto de servicio temporal se obtiene y se entrega a ObjectWatcher para supervisión.
- Enganche la devolución de llamada del bucle de mensajes del hilo principal
2.3 Cómo ObjectWatcher monitorea las pérdidas de memoria
2.3.1 Los cuatro conocimientos relacionados con referencias principales de Java
-
fuerte referencia
- La referencia más común es que cuando el objeto tiene una referencia fuerte y no está anulado, la máquina virtual no lo reciclará incluso si pasa a OOM.
-
referencia suave
- Cuando la memoria es suficiente, la máquina virtual no la reciclará. Cuando la memoria es insuficiente, la reciclará automáticamente. A menudo se utiliza para el almacenamiento en caché de imágenes.
SoftReference<String> softReference = new SoftReference<>(str);
-
cita débil
- Independientemente de si la memoria es suficiente, siempre que llame manualmente a la recolección de basura System.gc() o espere a que la máquina virtual realice GC automáticamente, las referencias débiles se reciclarán. Se utiliza principalmente para colocar en ubicaciones propensas a pérdidas de memoria, como Handler en Android.
WeakReference<String> weakReference = new WeakReference<>(str);
-
referencia virtual
- Las referencias virtuales no determinan el ciclo de vida del objeto. Si un objeto contiene solo referencias fantasmas, el recolector de basura puede reclamarlo en cualquier momento como si no tuviera referencias. Se utiliza principalmente para rastrear la actividad de los objetos reciclados por el recolector de basura, creados con PhantomReference.
-
Cola de referenciaCola de referencia
- Cuando gc está listo para reciclar un objeto, si descubre que solo tiene una referencia suave (o referencia débil o referencia virtual) apuntándolo, agregará esta referencia suave (o referencia débil o referencia virtual) antes de reciclar el objeto. objeto a la cola de referencia (ReferenceQueue) asociada con él. Si un objeto de referencia suave (o referencia débil o referencia virtual) está en la cola de referencia, significa que el objeto al que apunta el objeto de referencia se ha reciclado;
// 构造一个强引用
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 Monitoreo de objetos 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
}
}
- Pasó principalmente por tres pasos.
watchedObject
Cree una referencia débil para el objeto monitoreadoKeyedWeakReference
y guárdela en la tabla de mapeo de <UUID, KeyedWeakReference>;- PostDelay verifica si el objeto de referencia aparece en la cola de referencia después de cinco segundos. Si aparece en la cola, significa que el objeto monitoreado no se ha filtrado. Posteriormente, se eliminan los registros no filtrados en la tabla de mapeo y
retainedUptimeMillis
los campos de los objetos de referencia filtrados se actualizan para marcarlos como filtrados. onObjectRetained
Notificar a LeakCanary que se ha producido una nueva pérdida de memoria internamente mediante una devolución de llamada
2.4 Volcar el montón para obtener un archivo de instantánea de memoria
- Después de que ObjectWatcher determine que el objeto monitoreado se ha filtrado, volverá a
OnObjectRetainedListener.onObjectRetained()
llamar al administrador interno de LeakCanary a través del método de interfaz.InternalLeakCanary
- Dado que el trabajo de análisis lleva mucho tiempo, LeakCanary, naturalmente, no realizará el trabajo de análisis cada vez que encuentre un objeto de pérdida de memoria, sino que realizará dos intercepciones:
- 1. El recuento de objetos filtrados no alcanza el umbral, o el tiempo para entrar en segundo plano no alcanza el umbral.
- 2. La distancia de cálculo desde el último HeapDump no ha excedido los 60 segundos.
// 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 Analizar el volcado de instantáneas del montón
- LeakCanary generó con éxito
.hprof
un archivo de instantánea del montón y envió un evento interno de LeakCanaryHeapDump
. En los elementos de configuración de LeakCanary se configuran múltiples consumidores de eventos, EventListener. Estos tres seleccionarán la estrategia de ejecución óptima en función de las dependencias actuales de la aplicación: - 1 - Análisis multiproceso de WorkerManager
- 2 - Análisis asincrónico de WorkManager
- 3-Análisis de hilos asincrónicos (estrategia de encubrimiento)
// 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 Análisis multiproceso de 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 Análisis asincrónico de 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 Análisis de subprocesos asincrónicos comunes 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)
}
}
}
}
AndroidDebugHeapAnalyzer.runAnalysisBlocking
Se puede ver que entre los tres métodos, LeakCanary analiza la instantánea del montón a través del método de ejecución de subproceso o subproceso y envía eventos de devolución de llamada durante el proceso de análisis y después de que se completa el análisis.- El análisis de instantáneas del montón finalmente lo completa HeapAnalizer en Shark. El análisis no se realizará aquí. El proceso principal es:
- 1. Busque objetos filtrados en la instantánea del montón. El valor predeterminado es buscar objetos de tipo KeyedWeakReference;
- 2. Analice la cadena de referencia más corta del objeto KeyedWeakReference, agrúpela según la firma de la cadena de referencia y clasifíquela según fugas de aplicaciones y fugas de biblioteca;
- 3. Devuelve el evento de finalización del análisis.
3. Resumen
- Inicializar con ContentProvider
- Monitoreo de registros para 5 escenarios de fugas de Android
- La actividad utiliza application.registerActivityLifecycleCallbacks (devolución de llamada de ActivityLifecycleCallbacks) para monitorear el ciclo de vida global de la actividad
- Fragment primero
Application.registerActivityLifecycleCallbacks(…)
escucha el evento Activity.onCreate () a través de la interfaz y luegoFragmentManager.registerFragmentLifecycleCallbacks(…)
escucha el ciclo de vida de Fragment a través de la interfaz. - ViewModel se
Hook
implementa a través de ViewModelStore - El servicio se implementa a través de devoluciones de llamadas de Hook
mH.mCallback
y mensajes de monitoreo .STOP_SERVICE
WindowManagerGlobal
RootView se implementa internamente mediante el uso de la biblioteca Curtains para conectar el servicio WMS.
- Después de recibir la devolución de llamada de destrucción, cree una KeyedWeakReference basada en el objeto a reciclar y asóciela con ReferenceQueue. Espere 5 segundos para verificar si el objeto relevante se ha reciclado. De lo contrario, inicie el servicio y descargue el montón para obtener la memoria. archivo de instantánea
.hprof
. - Analice el archivo a través de la biblioteca Shark
.hprof
, obtenga el objeto filtrado y calcule la ruta más corta desde el objeto filtrado hasta las raíces de GC. - Combine múltiples rutas de fuga y resultados de análisis de salida, y muestre los resultados en la interfaz visual