1. LeakCanary 初始化
我们直接引入依赖即可,无需代码初始化,LeakCanary
会在 app 初始化时自动初始化自身。
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
Application->attachBaseContext
=====>
ContentProvider->onCreate
=====>
Application->onCreate
=====>
Activity->onCreate
/**
* Content providers are loaded before the application class is created. [AppWatcherInstaller] is
* used to install [leakcanary.AppWatcher] on application start.
*/
internal sealed class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
......
AppWatcher
是初始化观察对象的入口处:
@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
checkMainThread()
..
//此处 watchersToInstall 可根据需要自定义 --> eg:
//watchedObject !is BadSdkLeakingFragment
//it !is RootViewWatcher
watchersToInstall.forEach {
it.install()
}
}
manualInstall
方法的参数 watchersToInstall
默认实现 Activity
、Fragment
、View
、ViewModel
和 Service
的监听:
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
2. Watcher
2.1 ActivityWatcher
/**
* Expects activities to become weakly reachable soon after they receive the [Activity.onDestroy]
* callback.
*/
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"
)
}
}
//LeakCanary init时调用
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
//没找到调用此方法的地方
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
通过 application
监听 Activity
的生命周期,在 onDestory
回调reachabilityWatcher.expectWeaklyReachable
方法,做内存泄漏的检查工作。
2.2 FragmentAndViewModelWatcher
FragmentAndViewModelWatcher
主要检测了三种
- Fragments (Support Library, Android X and AOSP)
- Fragment#onDestroy()
- Fragment views (Support Library, Android X and AOSP)
- Fragment#onDestroyView()
- Android X view models (both activity and fragment view models)
- ViewModel#onCleared()
/**
* Expects:
* - Fragments (Support Library, Android X and AOSP) to become weakly reachable soon after they
* receive the Fragment#onDestroy() callback.
* - Fragment views (Support Library, Android X and AOSP) to become weakly reachable soon after
* fragments receive the Fragment#onDestroyView() callback.
* - Android X view models (both activity and fragment view models) to become weakly reachable soon
* after they received the ViewModel#onCleared() callback.
*/
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
}
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
watcher(activity)
}
}
}
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
...
- AndroidOFragmentDestroyWatcher
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)
}
2.3 RootViewWatcher
Expects root views to become weakly reachable soon after they are removed from the window
通过 OnAttachStateChangeListener
的 onViewAttachedToWindow
和onViewDetachedFromWindow
方法回调可做内存泄漏的检查工作:
/**
* Expects root views to become weakly reachable soon after they are removed from the window
* manager.
*/
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
}
}
2.4 ServiceWatcher
Service
通过hook ActivityThread的 H
类和 AMS
,当 AMS
调用 serviceDoneExecuting
方法可做内存泄漏的检查工作。
/**
* Expects services to become weakly reachable soon after they receive the [Service.onDestroy]
* callback.
*/
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
private val servicesToBeDestroyed = WeakHashMap<IBinder, WeakReference<Service>>()
...
override fun install() {
checkMainThread()
try {
//hook ActivityThread中H类的Callback
swapActivityThreadHandlerCallback { mCallback ->
..
Handler.Callback { msg ->
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
//在收到STOP_SERVICE时将Service以弱应用形式记录
activityThreadServices[key]?.let {
onServicePreDestroy(key, it)
}
}
mCallback?.handleMessage(msg) ?: false
}
}
//这里hook了AMS
swapActivityManager { activityManagerInterface, activityManagerInstance ->
...
//通过代理形式 在调用serviceDoneExecuting方法时可判断对象是否泄漏
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) { _, method, args ->
//serviceDoneExecuting is METHOD_SERVICE_DONE_EXECUTING
if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token)) {
onServiceDestroyed(token)
}
}
...
//method.invoke(activityManagerInstance)
}
}
} catch (ignored: Throwable) {
SharkLog.d(ignored) { "Could not watch destroyed services" }
}
}
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 代码
companion object {
private const val STOP_SERVICE = 116
private const val METHOD_SERVICE_DONE_EXECUTING = "serviceDoneExecuting"
}
override fun uninstall() {
checkMainThread()
uninstallActivityManager?.invoke()
uninstallActivityThreadHandlerCallback?.invoke()
uninstallActivityManager = null
uninstallActivityThreadHandlerCallback = null
}
}
2.5 OnObjectRetainedListener
Listener used by [ObjectWatcher] to report retained objects.
/**
* Listener used by [ObjectWatcher] to report retained objects.
*/
fun interface OnObjectRetainedListener {
/**
* A watched object became retained.
*/
fun onObjectRetained()
companion object {
inline operator fun invoke(crossinline block: () -> Unit): OnObjectRetainedListener =
object : OnObjectRetainedListener {
override fun onObjectRetained() {
block()
}
}
}
}
- InternalLeakCanary#invoke
此回调中会初始化一些检测内存泄露过程中需要的对象:
override fun invoke(application: Application) {
_application = application
checkRunningInDebuggableBuild()
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))
val gcTrigger = GcTrigger.Default// 配置项
val configProvider = { LeakCanary.config }
// 发起内存泄漏检测的线程
val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper)
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
configProvider
)
application.registerVisibilityListener { applicationVisible ->
this.applicationVisible = applicationVisible
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
}
registerResumedActivityListener(application)
addDynamicShortcut(application)
// We post so that the log happens after Application.onCreate()
mainHandler.post {
// https://github.com/square/leakcanary/issues/1981
// We post to a background handler because HeapDumpControl.iCanHasHeap() checks a shared pref
// which blocks until loaded and that creates a StrictMode violation.
backgroundHandler.post {
SharkLog.d {...}
}
}
}
- heapDumper 用于确认内存泄漏之后进行 heap dump 工作。
- gcTrigger 用于发现可能的内存泄漏之后手动调用 GC 确认是否真的为内存泄露。
这两个对象是 LeakCanary 检测内存泄漏的核心
2.6 ObjectWatcher
/**
* [ObjectWatcher] can be passed objects to [watch]. It will create [KeyedWeakReference] instances
* that reference watches objects, and check if those references have been cleared as expected on
* the [checkRetainedExecutor] executor. If not, these objects are considered retained and
* [ObjectWatcher] will then notify registered [OnObjectRetainedListener]s on that executor thread.
*
* [checkRetainedExecutor] is expected to run its tasks on a background thread, with a significant
* delay to give the GC the opportunity to identify weakly reachable objects.
*
* [ObjectWatcher] is thread safe.
*/
// Thread safe by locking on all methods, which is reasonably efficient given how often
// these methods are accessed.
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 {
private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>()
/**
* References passed to [watch].
*/
private val watchedObjects = mutableMapOf<String, KeyedWeakReference>()
private val queue = ReferenceQueue<Any>()
/**
* Returns true if there are watched objects that aren't weakly reachable, and
* have been watched for long enough to be considered retained.
*/
val hasRetainedObjects: Boolean
@Synchronized get() {
removeWeaklyReachableObjects()
return watchedObjects.any { it.value.retainedUptimeMillis != -1L }
}
/**
* Returns the number of retained objects, ie the number of watched objects that aren't weakly
* reachable, and have been watched for long enough to be considered retained.
*/
val retainedObjectCount: Int
@Synchronized get() {
removeWeaklyReachableObjects()
return watchedObjects.count { it.value.retainedUptimeMillis != -1L }
}
/**
* Returns true if there are watched objects that aren't weakly reachable, even
* if they haven't been watched for long enough to be considered retained.
*/
val hasWatchedObjects: Boolean
@Synchronized get() {
removeWeaklyReachableObjects()
return watchedObjects.isNotEmpty()
}
/**
* Returns the objects that are currently considered retained. Useful for logging purposes.
* Be careful with those objects and release them ASAP as you may creating longer lived leaks
* then the one that are already there.
*/
val retainedObjects: List<Any>
@Synchronized get() {
removeWeaklyReachableObjects()
val instances = mutableListOf<Any>()
for (weakReference in watchedObjects.values) {
if (weakReference.retainedUptimeMillis != -1L) {
val instance = weakReference.get()
if (instance != null) {
instances.add(instance)
}
}
}
return instances
}
@Synchronized fun addOnObjectRetainedListener(listener: OnObjectRetainedListener) {
onObjectRetainedListeners.add(listener)
}
@Synchronized fun removeOnObjectRetainedListener(listener: OnObjectRetainedListener) {
onObjectRetainedListeners.remove(listener)
}
/**
* Identical to [watch] with an empty string reference name.
*/
@Deprecated(
"Add description parameter explaining why an object is watched to help understand leak traces.",
replaceWith = ReplaceWith(
"expectWeaklyReachable(watchedObject, \"Explain why this object should be garbage collected soon\")"
)
)
fun watch(watchedObject: Any) {
expectWeaklyReachable(watchedObject, "")
}
@Deprecated(
"Method renamed expectWeaklyReachable() to clarify usage.",
replaceWith = ReplaceWith(
"expectWeaklyReachable(watchedObject, description)"
)
)
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)
}
}
/**
* Clears all [KeyedWeakReference] that were created before [heapDumpUptimeMillis] (based on
* [clock] [Clock.uptimeMillis])
*/
@Synchronized fun clearObjectsWatchedBefore(heapDumpUptimeMillis: Long) {
val weakRefsToRemove =
watchedObjects.filter { it.value.watchUptimeMillis <= heapDumpUptimeMillis }
weakRefsToRemove.values.forEach { it.clear() }
watchedObjects.keys.removeAll(weakRefsToRemove.keys)
}
/**
* Clears all [KeyedWeakReference]
*/
@Synchronized fun clearWatchedObjects() {
watchedObjects.values.forEach { it.clear() }
watchedObjects.clear()
}
@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)
}
}
3. 寻找泄露对象-ObjectWatcher
当对象在恰当的时机回收后,通过 ObjectWatcher
的expectWeaklyReachable
方法进行观察是否有泄露
3.1 检测内存泄漏的时机
- ActivityWatcher
- Activity#onActivityDestroyed
- FragmentAndViewModelWatcher
- Fragments (Support Library, Android X and AOSP)
- Fragment#onDestroy()
- Fragment views (Support Library, Android X and AOSP)
- Fragment#onDestroyView()
- Android X view models (both activity and fragment view models)
- ViewModel#onCleared()
- Fragments (Support Library, Android X and AOSP)
Expects root views to become weakly reachable soon after they are removed from the window
- RootViewWatcher
- rootView.addOnAttachStateChangeListener#onViewDetachedFromWindow
- ServiceWatcher
- AMS#serviceDoneExecuting
3.2 需要被检测是否内存泄露的对象 – WeakReferences 何时添加至监听队列?
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.
//主要看上面描述就够了
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)
}
3.3 寻找可能的泄漏对象
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
...
//移除非泄漏的弱引用对象
removeWeaklyReachableObjects()
//为此对象生成唯一随机id
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
...
//将此对象包成weakRefrence对象 然后加入队列中
watchedObjects[key] = reference
//5s后弱引用依然存在则有泄漏风险
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
3.4 检测泄漏的对象
- HeapDumpTrigger
private fun checkRetainedObjects() {
...
var retainedReferenceCount = objectWatcher.retainedObjectCount
//存在泄漏对象的话手动GC一次 然后再次检测
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
//GC 后重新计算 retainedReferenceCount
retainedReferenceCount = objectWatcher.retainedObjectCount
}
//如果泄漏数量不超出阈值(默认5) 结束
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
//泄漏数量 > 阈值(默认5),存在内存泄漏,生成hprof文件
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(
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
}
dismissRetainedCountNotification()
val visibility = if (applicationVisible) "visible" else "not visible"
//dump文件 通过原生Debug.dumpHprofData(heapDumpFile.absolutePath);
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
- retainedReferenceCount
/**
* Returns the number of retained objects, ie the number of watched objects that aren't weakly
* reachable, and have been watched for long enough to be considered retained.
*/
val retainedObjectCount: Int
@Synchronized get() {
removeWeaklyReachableObjects()
return watchedObjects.count { it.value.retainedUptimeMillis != -1L }
}
- 这一步并不会立刻触发
dump
生成hprof
文件分析,而是会先触发GC回收对象,再次判断未回收的对象数量 - 如果超出5个,通过原生方式生成hprof文件,再通过HeapAnalyzerService分析这个文件。
HeapAnalyzerService属于是square的另一个开源库shark,是一个前台服务,整个流程通过analyzeHeap方法分析文件,将分析出的结果包装成一个HeapAnalysisSuccess对象通过onHeapAnalyzedListener回调。
4. 分析 Hprof
hprof文件的标准协议主要由head和body组成,body是由一系列不同类型的Record组成,Record主要用于描述trace、object、thread等信息,依次分为4个部分:TAG、TIME、LENGTH、BODY,其中TAG就是表示Record类型。Record之间依次排列或嵌套,最终组成hprof文件。
多个Record被进抽象为HprofMemoryIndex,Index可以快速定位到对应对象在hprof文件中的位置;最终Index和Hprof一起再组成HprofGraph,graph做为hprof的最上层描述,将所有堆中数据抽象为了 gcRoots、objects、classes、instances等集合。
interface HeapGraph {
val gcRoots: List<GcRoot>
val objects: Sequence<HeapObject>
val classes: Sequence<HeapClass>
val instances: Sequence<HeapInstance>
val objectArrays: Sequence<HeapObjectArray>
...
}
后续通过 Graph
的 objects
集合找出泄露对象,然后通过广度优先遍历找出其到 GcRoot
的引用路径链,结束流程。
5. 总结
5.1 内存泄露的观察对象
不再需要的对象依然被引用,导致对象被分配的内存无法被回收 --> GC 回收机制
默认情况下 LeakCanary
会观察以下对象:
Activity
Fragment
RootView
Service
- ActivityWatcher
- Activity#onActivityDestroyed
- FragmentAndViewModelWatcher
- Fragments (Support Library, Android X and AOSP)
- Fragment#onDestroy()
- Fragment views (Support Library, Android X and AOSP)
- Fragment#onDestroyView()
- Android X view models (both activity and fragment view models)
- ViewModel#onCleared()
- Fragments (Support Library, Android X and AOSP)
- RootViewWatcher
- rootView.addOnAttachStateChangeListener#onViewDetachedFromWindow
- ServiceWatcher
- AMS#serviceDoneExecuting
5.2 如何触发检测
Activity
举例- app 运行时会 registerActivityLifecycleCallbacks
Activity
onDestory
调用reachabilityWatcher.expectWeaklyReachable
去检测内存泄漏
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
ActivityWatcher
fun interface ReachabilityWatcher {
/**
* Expects the provided [watchedObject] to become weakly reachable soon. If not,
* [watchedObject] will be considered retained.
*/
fun expectWeaklyReachable(
watchedObject: Any,
description: String
)
}
- ObjectWatcher#expectWeaklyReachable
@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)
}
}
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)
}
可以看出
- 传入的观察对象都会被存储在watchedObjects中
- 会为每个watchedObject生成一个KeyedWeakReference弱引用对象并与一个queue关联,当对象被回收时,该弱引用对象将进入queue当中
- 在检测过程中,我们会调用多次removeWeaklyReachableObjects,将已回收对象从watchedObjects中移除
- 如果watchedObjects中没有移除对象,证明它没有被回收,那么就会调用moveToRetained
5.3 checkRetainedInstances() 方法去确认泄露
- ObjectWatcher#moveToRetained
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
- OnObjectRetainedListener#onObjectRetained
/**
* Listener used by [ObjectWatcher] to report retained objects.
*
* This is a functional interface with which you can create a [OnObjectRetainedListener] from a lambda.
*/
fun interface OnObjectRetainedListener {
/**
* A watched object became retained.
*/
fun onObjectRetained()
...
}
- InternalLeakCanary#
override fun onObjectRetained() = scheduleRetainedObjectCheck()
fun scheduleRetainedObjectCheck() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.scheduleRetainedObjectCheck()
}
}
- HeapDumpTrigger#scheduleRetainedObjectCheck
fun scheduleRetainedObjectCheck(
delayMillis: Long = 0L
) {
val checkCurrentlyScheduledAt = checkScheduledAt
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
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
val nopeReason = iCanHasHeap.reason()
val wouldDump = !checkRetainedCount(
retainedReferenceCount, config.retainedVisibleThreshold, nopeReason
)
if (wouldDump) {
val uppercaseReason = nopeReason[0].toUpperCase() + nopeReason.substring(1)
onRetainInstanceListener.onEvent(DumpingDisabled(uppercaseReason))
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = uppercaseReason
)
}
}
...
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
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(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
- 如果retainedObjectCount数量大于0,则进行一次GC,避免额外的Dump
- 默认情况下,如果retainedReferenceCount<5,不会进行Dump,节省资源
- 如果两次Dump之间时间少于60s,也会直接返回,避免频繁Dump
- 调用heapDumper.dumpHeap()进行真正的Dump操作
- Dump之后,要删除已经处理过了的引用
- 调用HeapAnalyzerService.runAnalysis对结果进行分析
对象应该被销毁时观察,eg: Activity onDestory() 时添加至 WeakRefrence 观察队列(参考3.3),五秒后该对象还在观察队列中代表可能存在内存泄漏
5.4 为什么不能用于线上
- 每次内存泄漏以后,都会生成一个.hprof文件,然后解析,并将结果写入.hprof.result。增加手机负担,引起手机卡顿等问题。
- 多次调用GC,可能会对线上性能产生影响
- 同样的泄漏问题,会重复生成 .hprof 文件,重复分析并写入磁盘。
- hprof文件较大,信息回捞成问题。