0
点赞
收藏
分享

微信扫一扫

LeakCanary 2.7 原理分析


  • ​​1. LeakCanary 初始化​​
  • ​​2. Watcher​​
  • ​​2.1 ActivityWatcher​​
  • ​​2.2 FragmentAndViewModelWatcher​​
  • ​​2.3 RootViewWatcher​​
  • ​​2.4 ServiceWatcher​​
  • ​​2.5 OnObjectRetainedListener​​
  • ​​2.6 ObjectWatcher​​
  • ​​3. 寻找泄露对象-ObjectWatcher​​
  • ​​3.1 检测内存泄漏的时机​​
  • ​​3.2 需要被检测是否内存泄露的对象 -- WeakReferences 何时添加至监听队列?​​
  • ​​3.3 寻找可能的泄漏对象​​
  • ​​3.4 检测泄漏的对象​​
  • ​​4. 分析 Hprof​​
  • ​​5. 总结​​
  • ​​5.1 内存泄露的观察对象​​
  • ​​5.2 如何触发检测​​
  • ​​5.3 checkRetainedInstances() 方法去确认泄露​​
  • ​​5.4 为什么不能用于线上​​
  • ​​6. 参考链接​​

1. LeakCanary 初始化

我们直接引入依赖即可,无需代码初始化,​​LeakCanary​​ 会在 app 初始化时自动初始化自身。

debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'

->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}
)
}
}

//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} +
"(references to its views should be cleared to prevent leaks)"
)
}
}

override fun onFragmentDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
reachabilityWatcher.expectWeaklyReachable(
fragment, "${fragment::class.java.name}
)
}
}

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}
)
}

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}
)
}
}
}

//省略 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()

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()
  • 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}
)
}
}

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)
}

可以看出

  1. 传入的观察对象都会被存储在watchedObjects中
  2. 会为每个watchedObject生成一个KeyedWeakReference弱引用对象并与一个queue关联,当对象被回收时,该弱引用对象将进入queue当中
  3. 在检测过程中,我们会调用多次removeWeaklyReachableObjects,将已回收对象从watchedObjects中移除
  4. 如果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"
)
}

  1. 如果retainedObjectCount数量大于0,则进行一次GC,避免额外的Dump
  2. 默认情况下,如果retainedReferenceCount<5,不会进行Dump,节省资源
  3. 如果两次Dump之间时间少于60s,也会直接返回,避免频繁Dump
  4. 调用heapDumper.dumpHeap()进行真正的Dump操作
  5. Dump之后,要删除已经处理过了的引用
  6. 调用HeapAnalyzerService.runAnalysis对结果进行分析

对象应该被销毁时观察,eg: Activity onDestory() 时添加至 WeakRefrence 观察队列(参考3.3),五秒后该对象还在观察队列中代表可能存在内存泄漏

LeakCanary 2.7 原理分析_初始化

5.4 为什么不能用于线上

  1. 每次内存泄漏以后,都会生成一个.hprof文件,然后解析,并将结果写入.hprof.result。增加手机负担,引起手机卡顿等问题。
  2. 多次调用GC,可能会对线上性能产生影响
  3. 同样的泄漏问题,会重复生成 .hprof 文件,重复分析并写入磁盘。
  4. hprof文件较大,信息回捞成问题。

6. 参考链接

  • ​​全新 LeakCanary 2 ! 完全基于 Kotlin 重构升级 !​​
  • ​​From Erren - GC 回收机制​​
  • ​​LeakCanary 新版 2.x ,你应该知道的知识点​​


举报

相关推荐

0 条评论