前言
本文旨在梳理整个工作流程,文中源码基于 LeakCanary 2.7 版本。
简介
1.什么是LeakCanary
LeakCanary 是 square 公司开源的内存泄漏检测工具。
2.什么是内存泄漏
内存泄漏是指没有用的对象资源仍与 GcRoot 保持可达路径,导致系统无法进行回收,继而消耗资源。
流程解析
1.注册及寻找观察时机
LeakCanary 注册是利用 ContentProvider 的 onCreate 生命周期处于 Application 的 onCreate 生命周期之前的特性,将注册部分写在 ContentProvider 中,这样做免去手动写代码注册,但是会影响启动速度,所以一般在 debug 时使用。
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)
) {
...
watchersToInstall.forEach {
//依次初始化各个Watcher
it.install()
}
}
//添加到集合中的的观察者
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
manualInstall
方法的参数watchersToInstall
默认实现 Activity、Fragment、View、ViewModel 和 Service 的对象监听,接下来我们一一看看这几个 Watcher 是如何实现的。
1.1 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) {
//每个 activity onDestoryed 后,即可观察此对象后续是否有泄露风险
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
//注册lifecycle生命周期回调
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
不难看出 ActivityWatcher 通过application.ActivityLifecycleCallbacks
监听 Activity 生命周期回调,在每个Activity.onDestory
后调用expectWeaklyReachable
方法(此方法后续解释)来观察对象是否被释放。
1.2 FragmentAndViewModelWatcher
FragmentAndViewModelWatcher 源码如下:
class FragmentAndViewModelWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val fragmentDestroyWatchers: List<(Activity) -> Unit> = run {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
//sdk在 8.0 及以上的 fragment
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
//向 activity.fragmentManager 注册 fragmentLifecycler 在 onFragmentViewDestroyed 和 onFragmentDestroyed 后检查对象是否泄漏
AndroidOFragmentDestroyWatcher(reachabilityWatcher)
)
}
//androidX 包中的 fragment
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
reachabilityWatcher
)?.let {
fragmentDestroyWatchers.add(it)
}
//support 包中的 fragment
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)
}
}
}
//注册时先通过activity onActivityCreated 再来注册fragment
override fun install() {
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
Fragment 的 onDestory()
和 onFragmentViewDestory()
监听。
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)
}
}
1.3 RootViewWatcher
RootViewWatcher 源代码如下:
class RootViewWatcher(
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val listener = OnRootViewAddedListener { rootView ->
//通过区分 rootview 的类型,判断是否需要检测,
//假如写一个单例的Dialog仅使用show()和hide()控制,而不会销毁对象,不属于泄漏)
val trackDetached = when(rootView.windowType) {
PHONE_WINDOW -> {
when (rootView.phoneWindow?.callback?.wrappedCallback) {
is Activity -> false
is Dialog -> rootView.resources.getBoolean(R.bool.leak_canary_watcher_watch_dismissed_dialogs)
else -> true
}
}
POPUP_WINDOW -> false
TOOLTIP, TOAST, UNKNOWN -> true
}
if (trackDetached) {
//在rootView detachedFromWindow 开始检测是否泄漏
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
}
}
通过OnAttachStateChangeListener
的onViewAttachedToWindow
和onViewDetachedFromWindow
方法回调可在后续做内存泄漏的检查工作。
1.4 ServiceWatcher
Service 通过 hook ActivityThread 的 H 类和 AMS,当 AMS 调用serviceDoneExecuting
方法后检测是否有泄漏的风险。
class ServiceWatcher(private val reachabilityWatcher: ReachabilityWatcher) : InstallableWatcher {
...
override fun install() {
...
try {
//hook ActivityThread中H类的Callback
swapActivityThreadHandlerCallback { mCallback ->
uninstallActivityThreadHandlerCallback = {
swapActivityThreadHandlerCallback {
mCallback
}
}
//在收到STOP_SERVICE时将Service以弱应用形式记录
Handler.Callback { msg ->
if (msg.what == STOP_SERVICE) {
val key = msg.obj as IBinder
activityThreadServices[key]?.let {
onServicePreDestroy(key, it)
}
}
mCallback?.handleMessage(msg) ?: false
}
}
//这里hook了AMS
swapActivityManager { activityManagerInterface, activityManagerInstance ->
uninstallActivityManager = {
swapActivityManager { _, _ ->
activityManagerInstance
}
}
//通过代理形式 在调用serviceDoneExecuting方法时可判断对象是否泄漏
Proxy.newProxyInstance(
activityManagerInterface.classLoader, arrayOf(activityManagerInterface)
) { _, method, args ->
if (METHOD_SERVICE_DONE_EXECUTING == method.name) {
val token = args!![0] as IBinder
if (servicesToBeDestroyed.containsKey(token)) {
onServiceDestroyed(token)
}
}
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 onServiceDestroyed(token: IBinder) {
servicesToBeDestroyed.remove(token)?.also { serviceWeakReference ->
serviceWeakReference.get()?.let { service ->
reachabilityWatcher.expectWeaklyReachable(
service, "${service::class.java.name} received Service#onDestroy() callback"
)
}
}
}
...省略了hook代码部分
}
1.5 小结
上述主要是添加需要检测的对象,以及寻找每个对象的回收时机点:
- Activity:
Activity#onActivityDestroyed
- Fragment:
Fragment#onDestroy()
- FragmentView:
Fragment#onDestoryView()
- ViewModel:
ViewModle#onClear()
- RootView:
addOnAttachStateChangeListener#onViewDetachedFromWindow
- Service:
AMS#serviceDoneExecuting
假如我们有个别自己需要观察的对象,即可在对象销毁后通过expectWeaklyReachable
来检测是否有泄露风险。
2. 寻找泄露对象-ObjectWatcher
当对象在恰当的时机回收后,通过ObjectWatcher
的expectWeaklyReachable
方法进行观察是否有泄露风险
@Synchronized override fun expectWeaklyReachable(
watchedObject: Any,
description: String
) {
//先移除一次非泄漏的弱引用对象
removeWeaklyReachableObjects()
val key = UUID.randomUUID().toString()
val watchUptimeMillis = clock.uptimeMillis()
//将观察对象包成 weakRefrence 对象,并注册到给定的队列中
//注:弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
watchedObjects[key] = reference
//异步检查观察对象
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
//5s后再次检查观察对象是否存活
fun scheduleRetainedObjectCheck(
delayMillis: Long = 0L
) {
...
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed({
checkScheduledAt = 0
checkRetainedObjects()
}, delayMillis)
}
再一次检查泄露对象
private fun checkRetainedObjects() {
...
//retainedObjectCount.get()会再次移除弱引用对象并得出未被移除的数量
var retainedReferenceCount = objectWatcher.retainedObjectCount
//存在泄漏对象的话手动GC一次 然后再次检测数量
if (retainedReferenceCount > 0) {
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
//如果泄漏数量不超出阈值(默认5) return
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
...
//dump堆文件
dumpHeap(
retainedReferenceCount = retainedReferenceCount,
retry = true,
reason = "$retainedReferenceCount retained objects, app is $visibility"
)
}
这一步先触发 GC 回收对象,再次判断未回收的对象数量,如果超出默认数量5个,通过 Android 的方式生成 hprof 文件,再通过 HeapAnalyzerService 分析这个文件。
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean,
reason: String
) {
...
//这里通过 Android 原生方法 Debug.dumpHprofData(heapDumpFile.absolutePath);获取文件
when (val heapDumpResult = heapDumper.dumpHeap()) {
is NoHeapDump -> {
...
}
is HeapDump -> {
lastDisplayedRetainedObjectCount = 0
lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
//得到dump文件后 开始分析dump文件
HeapAnalyzerService.runAnalysis(
context = application,
heapDumpFile = heapDumpResult.file,
heapDumpDurationMillis = heapDumpResult.durationMillis,
heapDumpReason = reason
)
}
}
}
fun runAnalysis(
context: Context,
heapDumpFile: File,
heapDumpDurationMillis: Long? = null,
heapDumpReason: String = "Unknown"
) {
val intent = Intent(context, HeapAnalyzerService::class.java)
intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
intent.putExtra(HEAPDUMP_REASON_EXTRA, heapDumpReason)
heapDumpDurationMillis?.let {
intent.putExtra(HEAPDUMP_DURATION_MILLIS_EXTRA, heapDumpDurationMillis)
}
startForegroundService(context, intent)
}
HeapAnalyzerService 属于是 square 的另一个开源库 shark,是一个前台服务,整个流程通过analyzeHeap
方法分析文件,将分析出的结果包装成一个 HeapAnalysisSuccess 对象通过onHeapAnalyzedListener
回调。
3. 分析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 的引用路径链,结束流程。
总结
LeakCanary 通过 ContentProvider 注册,在对象被销毁时利用 WeakRefrence + RefrenceQueue + GC观察泄露对象,dump hprof文件,通过shark库分析文件,查找泄露对象引用路径。
通过对于整体流程的理解后,可知 LeakCanary 并不适合于线上使用,在其使用时间会导致频繁 GC,生成分析 hprof 文件都会占用资源进而影响用户,且 hprof 文件一般都很大,上传前还需要进一步压缩。