Leakcanary2.0
Leakcanary
使用
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-2'
原理
Reference 、ReferenceQueue 详解
ReferenceQueue
引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中
WeakReference
weakReference = new WeakReference (list, new ReferenceQueue >());
WeakReference 创建时,如果指定一个ReferenceQueue 对象,在垃圾回收器检测到被引用对象的可到达性更改后,垃圾回收器会将已注册的引用对象添加到ReferenceQueue队列中,等待ReferenceQueue处理。但是如果当 GC 过后引用对象仍然不被加入ReferenceQueue中,就有可能存在内存泄漏问题。
实现无需初始化启动
【译】你的Android库是否还在Application中初始化?
大概原理,gradle打包会将不同的manifest文件合并,从而在启动application的时候带动provider,实现初始化。
AppWatcherInstaller.kt
internal sealed class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
InternalAppWatcher.install(application)
return true
}
}
分析代码
InternalAppWatcher.kt
fun install(application: Application) {
SharkLog.logger = DefaultCanaryLog()
checkMainThread()
if (this::application.isInitialized) {
return
}
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
onAppWatcherInstalled(application)
}
1.安装Activity监听
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
构造activityDestroyWatcher对象,并注册到application的lifecyclecallbacks中
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
if (configProvider().watchActivities) {
objectWatcher.watch(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
}
configProvider()对应AppWather
objectWatcher是在install传入的参数,和1.5版本leakcanary原理类似,依旧是在Application的lifecycle执行onActivityDestroyed回调时,进行监控watch()
1.1 ObjectWatcher#watch
@Synchronized fun watch(
watchedObject: Any,
description: String
) {
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
watchedObjects[key] = reference
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
@Synchronized fun watch(watchedObject: Any) {
watch(watchedObject, "")
}
熟悉的味道
- removeWeaklyReachableObjects()
移除弱可达的对象,在GC发生之前 - val reference =KeyedWeakReference() ObjectWatcher通过其来判断是否是若可达,用key来追踪是否关联ReferenceQueue
- moveToRetained(key)
先removeWeaklyReachableObjects,在执行所有onObjectRetainedListeners的onObjectRetained()
1.1.1 removeWeaklyReachableObjects()
首先清理一次watchedObjects中已经在保存在ReferenceQueue的引用,不再监控。
弱引用一旦变得弱可达,就会立即入队。这将在 finalization 或者 GC 之前发生。
也就是说,会被 GC 回收的对象引用,会保存在队列 queue 中。
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)
}
1.1.2 val reference =KeyedWeakReference()
KeyedWeakReference弱引用
A weak reference used by [ObjectWatcher] to determine which objects become weakly reachable
and which don't. [ObjectWatcher] uses [key] to keep track of [KeyedWeakReference] instances that
haven't made it into the associated [ReferenceQueue] yet.
随机生成key,将watchUptimeMillis,key,queue传入KeyedWeakReference中,构造出reference对象
大概可以理解为
WeakReference reference = new WeakReference(activity实例, new ReferenceQueue());
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
watchedObjects[key] = reference
将reference加入watchedObjects观察的map中。
1.1.3 moveToRetained(key)
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
再removeWeaklyReachableObjects()一遍,如果对应key的监控对象并没有remove掉(加入queue队列),则很可能是泄漏,会继续进行heapDumpTrigger操作。
InternalLeakCanary#onObjectRetained()
override fun onObjectRetained() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.onObjectRetained()
}
}
HeapDumpTrigger#onObjectRetained()
fun onObjectRetained() {
scheduleRetainedObjectCheck(
reason = "found new object retained",
rescheduling = false
)
}
private fun scheduleRetainedObjectCheck(
reason: String,
rescheduling: Boolean,
delayMillis: Long = 0L
) {
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects(reason)
}, delayMillis)
}
reason = "found new object retained",
checkRetainedObjects(reason)
private fun checkRetainedObjects(reason: String) {
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0) { //判断剩余引用的数量
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
onRetainInstanceListener.onEvent(DebuggerIsAttached)
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(
R.string.leak_canary_notification_retained_debugger_attached
)
)//发notification
scheduleRetainedObjectCheck(
reason = "debugger is attached",
rescheduling = true,
delayMillis = WAIT_FOR_DEBUG_MILLIS
)
return
}
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
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(
reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
rescheduling = true,
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
SharkLog.d { "Check for retained objects found $retainedReferenceCount objects, dumping the heap" }
dismissRetainedCountNotification()
dumpHeap(retainedReferenceCount, retry = true)
}
判断剩余引用数量 > 0 时,gcTrigger.runGc()
同1.5时,常规做法
override fun runGc() {
// System.gc() does not garbage collect every time. Runtime.gc() is
// more likely to perform a gc.
Runtime.getRuntime()
.gc()
enqueueReferences()
System.runFinalization()
}
检查剩余引用数量 checkRetainedCount()
retainedVisibleThreshold 默认是5
/**
* When the app is visible, LeakCanary will wait for at least
* [retainedVisibleThreshold] retained instances before dumping the heap. Dumping the heap
* freezes the UI and can be frustrating for developers who are trying to work. This is
* especially frustrating as the Android Framework has a number of leaks that cannot easily
* be fixed.
*
* When the app becomes invisible, LeakCanary dumps the heap after
* [AppWatcher.Config.watchDurationMillis] ms.
*
* The app is considered visible if it has at least one activity in started state.
*
* A higher threshold means LeakCanary will dump the heap less often, therefore it won't be
* bothering developers as much but it could miss some leaks.
*
* Defaults to 5.
*/
private fun checkRetainedCount(
retainedKeysCount: Int,
retainedVisibleThreshold: Int
): Boolean {
val countChanged = lastDisplayedRetainedObjectCount != retainedKeysCount
lastDisplayedRetainedObjectCount = retainedKeysCount
if (retainedKeysCount == 0) {//如果没有剩余引用 return true
SharkLog.d { "Check for retained object found no objects remaining" }
if (countChanged) {
//判断和上次display的数量是否一只,不一致show notification
onRetainInstanceListener.onEvent(NoMoreObjects)
showNoMoreRetainedObjectNotification()
}
return true
}
//如果小于5
if (retainedKeysCount < retainedVisibleThreshold) {
if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
if (countChanged) {
//发Event
onRetainInstanceListener.onEvent(BelowThreshold(retainedKeysCount))
}
//发notification
showRetainedCountNotification(
objectCount = retainedKeysCount,
contentText = application.getString(
R.string.leak_canary_notification_retained_visible, retainedVisibleThreshold
)
)
//继续安排剩余object检查 reason不一样了 delay = 2s
scheduleRetainedObjectCheck(
reason = "found only $retainedKeysCount retained objects (< $retainedVisibleThreshold while app visible)",
rescheduling = true,
delayMillis = WAIT_FOR_OBJECT_THRESHOLD_MILLIS
)
return true
}
}
//大于5 false 继续进行
return false
}
判断是否isDebuggerAttached
(这个我不懂)
scheduleRetainedObjectCheck(
reason = "debugger is attached",
rescheduling = true,
delayMillis = WAIT_FOR_DEBUG_MILLIS
)
如果上次dump 和现在dump的时间 < 60s 继续 ,然后return
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
scheduleRetainedObjectCheck(
reason = "previous heap dump was ${elapsedSinceLastDumpMillis}ms ago (< ${WAIT_BETWEEN_HEAP_DUMPS_MILLIS}ms)",
rescheduling = true,
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
dumpHeap(retainedReferenceCount, retry = true)
判断有没有hprof,如果没有重新scheduleRetainedObjectCheck,如果有dumpheap
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean
) {
saveResourceIdNamesToMemory()
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
val heapDumpFile = heapDumper.dumpHeap()
if (heapDumpFile == null) {
if (retry) {
SharkLog.d { "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms" }
scheduleRetainedObjectCheck(
reason = "failed to dump heap",
rescheduling = true,
delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
)
} else {
SharkLog.d { "Failed to dump heap, will not automatically retry" }
}
return
}
lastDisplayedRetainedObjectCount = 0
lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
HeapAnalyzerService.runAnalysis(application, heapDumpFile)
}
2.安装fragment监听
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
watcher(activity)
}
}
})
2.1 AndroidOFragmentDestroyWatcher
- onFragmentViewDestroyed
- onFragmentDestroyed
在以上两个回调时,通过ObjectWatcher监视fragment 和view 对象,ObjectWatcher之后逻辑相同。
internal class AndroidOFragmentDestroyWatcher(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
) : (Activity) -> Unit {
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
val view = fragment.view
if (view != null && configProvider().watchFragmentViews) {
objectWatcher.watch(
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
) {
if (configProvider().watchFragments) {
objectWatcher.watch(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}
}
}
3.处理初始化工作
onAppWatcherInstalled(application)
onAppWatcherInstalled对应init中internalLeakCanary对象,可以看到反射调用。
init {
val internalLeakCanary = try {
val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
leakCanaryListener.getDeclaredField("INSTANCE")
.get(null)
} catch (ignored: Throwable) {
NoLeakCanary
}
@kotlin.Suppress("UNCHECKED_CAST")
onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
}
3.1 InternalLeakCanary#invoke
override fun invoke(application: Application) {
this.application = application
checkRunningInDebuggableBuild()
//注册listener
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
//创建 AndroidHeapDumper
val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
//初始化gctrigger
val gcTrigger = GcTrigger.Default
val configProvider = { LeakCanary.config }
val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
handlerThread.start()
//初始化handler 可以看到绑定的looper
val backgroundHandler = Handler(handlerThread.looper)
//初始化HeapDumpTrigger
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
configProvider
)
application.registerVisibilityListener { applicationVisible ->
this.applicationVisible = applicationVisible
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
}
registerResumedActivityListener(application)
addDynamicShortcut(application)
disableDumpHeapInTests()
}
以上进行了一堆的初始化操作,比较重要的只有一条
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
3.2 HeapDumpTrigger#onApplicationVisibilityChanged
这里解释了leakcanary如何自动监听内存泄漏
判断applicationvisible是否可见。如果不可见scheduleRetainedObjectCheck,delay 默认5s
fun onApplicationVisibilityChanged(applicationVisible: Boolean) {
if (applicationVisible) {
applicationInvisibleAt = -1L
} else {
applicationInvisibleAt = SystemClock.uptimeMillis()
// Scheduling for after watchDuration so that any destroyed activity has time to become
// watch and be part of this analysis.
scheduleRetainedObjectCheck(
reason = "app became invisible",
rescheduling = false,
delayMillis = AppWatcher.config.watchDurationMillis
)
}
}
Android开发者,是时候了解LeakCanary2.0了
LeakCanary 2.0原理