LeakCanary使用只需在app中的build.gradle添加依赖
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
没错,一行搞定!
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false"/>
internal sealed class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
//进行初始化
AppWatcher.manualInstall(application)
return true
}
}
apk打包流程中会把这个provider合并到app下的mainfest文件中,ContentProvider的onCreate比Application的onCreate早执行,调用AppWatcher.manualInstall(application)进行初始化的。
//AppWatcher
fun manualInstall(
application: Application,
//5s,后面checkRetainedExecutor.execute有用到
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
//...
watchersToInstall.forEach {
it.install()
}
}
//初始化4个watcher
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
LeakCanary会Activity、Fragment、Fragment的view、ViewModel、RootView和Service纳入检测。
监听泄漏的时机
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通过registerActivityLifecycleCallbacks监听Activity生命周期回调,在onActivityDestroyed时,调用objectWatcher.expectWeaklyReachable将Activity纳入检测
FragmentAndViewModelWatcher
兼容了O以上、AndroidX、Support,通过fragmentManager.registerFragmentLifecycleCallbacks监听,在onFragmentViewDestroyed与onFragmentDestroyed中调用expectWeaklyReachable纳入检测。
对于ViewModel,在AndroidXFragmentDestroyWatcher里还会额外监听
ViewModelClearedWatcher.install(activity, reachabilityWatcher)
反射获取ViewModelStore的mMap, 在ViewModelClearedWatcher的onCleared中调用expectWeaklyReachable将ViewModel纳入检测。
RootViewWatcher
通过反射获取WindowManagerGlobal中的mViews,再通过addOnAttachStateChangeListener监听rootView,在onViewDetachedFromWindow时执行expectWeaklyReachable纳入检测。
ServiceWatcher
1.反射获取ActivityThread中的mServices(app中全部Service的一个Map)。
2.反射获取名为H的Handler(Android消息机制中转中心)。
3.替换H的mCallBack实现,当消息为STOP_SERVICE时,便从mServices取出该消息对应的Service作为待检测Service引用。
4.Hook AMS,通过动态代理修改它的serviceDoneExecuting方法,在onServiceDestroyed时执行expectWeaklyReachable纳入检测。
如何检测内存泄漏?
原理:Java中的WeakReference表示弱引用,当GC时,它所持有的对象如果没有被其它强引用持有,那么它所引用的对象就会被回收,这个WeakReference会被加入到关联的ReferenceQueue。
最终都是调用了expectWeaklyReachable纳入检测
//核心代码片段 ObjectWatcher.kt
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
private val queue = ReferenceQueue<Any>()
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
//遍历queue,从watchedObjects删除已回收的对象
removeWeaklyReachableObjects()
//生成一个uuid作为key
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
//构建当前引用的弱引用对象,并关联引用队列queue
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
//将构建的弱引用存入watchedObjects
watchedObjects[key] = reference
checkRetainedExecutor.execute {
//Handler.postDelayed 实现延迟5s执行
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() {
var ref: KeyedWeakReference?
do {
//队列queue中的对象都是会被GC的
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
//说明释放了,从watchedObjects删除被回收的对象(移除watchedObjects集合中被GC的ref对象,剩下的就可能是泄漏的对象)
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
最后检查对象没哟被回收的话,调用onObjectRetained()方法
onObjectRetained
—>InternalLeakCanary.scheduleRetainedObjectCheck()
—>HeapDumpTrigger.scheduleRetainedObjectCheck
—>HeapDumpTrigger.scheduleRetainedObjectCheck
private fun checkRetainedObjects() {
//...
val config = configProvider()
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) {
//调用Runtime.getRuntime().gc()执行一次GC,再来看还剩下多少对象未被回收
//GC后Thread.sleep(100)确保对象被GC 等回收的引用入队
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
//当前泄漏实例<5,不进行heap dump
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
//1分钟内dump过,等会再来
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"
)
}
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean,
reason: String
) {
saveResourceIdNamesToMemory()
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
//调用AndroidHeapDumper的dumpHeap()方法—>Debug.dumpHprofData(heapDumpFile.absolutePath)
when (val heapDumpResult = heapDumper.dumpHeap()) {
is HeapDump -> {
lastDisplayedRetainedObjectCount = 0
lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
//清除这次dump之前的引用
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
//通过HeapAnalyzerService 去分析 heap ( 使用Shark库对heap进行分析)
HeapAnalyzerService.runAnalysis(
context = application,
heapDumpFile = heapDumpResult.file,
heapDumpDurationMillis = heapDumpResult.durationMillis,
heapDumpReason = reason
)
}
}
}
总结
1.如何初始化
apk打包流程中会把AppWatcherInstaller这个provider合并到app下的mainfest文件中,ContentProvider的onCreate比Application的onCreate早执行,调用AppWatcher.manualInstall(application)进行初始化的
2.检测时机
对象 | 如何获取引用 | 何时纳入检测 |
---|---|---|
Activity | ActivityLifecycleCallbacks回调 | onActivityDestroyed |
Fragment | FragmentLifecycleCallbacks回调 | onFragmentDestroyed |
Fragment中的View | FragmentLifecycleCallbacks回调 | onFragmentViewDestroyed |
ViewModel | 反射获取ViewModelStore的mMap | ViewModel的onCleared |
RootView | 反射获取WindowManagerGlobal中的mViews | onViewDetachedFromWindow |
Service | Hook H的mCallback实现,当消息为STOP_SERVICE时,从ActivityThread中的mServices获取 | onServiceDestroyed |
3.检测原理
当jvm进行垃圾回收时,无论内存是否充足,如果该对象只有弱引用存在,那么就会被垃圾回收器回收,同时该引用会被加入到关联的ReferenceQueue。
LeakCanary利用弱引用的特性,获取当前引用,构建弱引用对象KeyedWeakReference并关联一个ReferenceQueue,保存到watchedObjects中。GC后,通过key删除已经回收的对象,剩下的对象存在泄漏嫌疑。
网友评论