写在前面
leakcanary版本是2.4.0
用法很简单, 就不再说了.
使用完后, 有几个问题
- 为何桌面会多出来一个Leaks的app icon? 是安装了一个app吗?
- 怎么监听Activity的?
- 怎么判断对象泄漏的?
前置知识
WeakReference和ReferenceQueue
WeakReference
gc时, 不管内存空间是否足够, 都会回收对象的内存.
ReferenceQueue
WeakReference有一个两参的构造函数WeakReference#WeakReference(T, java.lang.ref.ReferenceQueue super T>), 第二个参数就是ReferenceQueue. 它的作用是, 如果gc时, 该对象成功被垃圾回收器回收, 那系统将会把他放到ReferenceQueue中.也即是, 提供了一个机制, 可以告知你, 哪些对象被回收. 注意: ReferenceQueue中出现的是被回收的对象.
为何桌面会多出来一个Leaks的app icon
这个Leaks的app icon并不是另外安装的apk, 而是系统提供的机制, 允许同一个app在桌面上有多个入口. 其使用方法可以参考这里
leakcanary的中使用的是activity-alias
leakcanary-android-core/src/main/AndroidManifest.xml如下
流程梳理
启动
leakcanary不需要在Application中初始化, 我们应该可以想到, leakcanary用的是ContentProvider
所以, 我们在源码AndroidManifest.xml文件中找ContentProvider
leakcanary-android-process/src/main/AndroidManifest.xml如下
leakcanary.internal.AppWatcherInstaller#onCreate
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
AppWatcher.manualInstall就是其初始化逻辑.
顺着APP的调用链, 最终执行了leakcanary.internal.InternalAppWatcher#install
leakcanary.internal.InternalAppWatcher#install
fun install(application: Application) {
//如果不是主线程, 就抛出异常
checkMainThread()
//如果已经初始化过, 就不再初始化
//防止重复初始化
if (this::application.isInitialized) {
return
}
SharkLog.logger = DefaultCanaryLog()
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
//监听Activity
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
//监听Fragment
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
onAppWatcherInstalled(application)
}
这是我们遇到的第一个重要方法.
其实里面的大部分方法在第一遍的时候都不需要细看, 我们先抓主干.
重点是ActivityDestroyWatcher.install和FragmentDestroyWatcher.install
ActivityDestroyWatcher.install
leakcanary.internal.ActivityDestroyWatcher的代码很简单
internal class ActivityDestroyWatcher private constructor(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
) {
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"
)
}
}
}
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
}
简单来说就是
Application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
//既然这个Activity执行了onDestroy, 那它就该被回收
//如果没被回收, 那该Activity就泄漏了
}
});
小结一下:
- 使用Application#registerActivityLifecycleCallbacks方法监听Activity的销毁, Activity执行onDestroy后, 开始监听该Activity
- 监听逻辑在ObjectWatcher#watch
leakcanary.internal.FragmentDestroyWatcher#install
FragmentDestroyWatcher#install与ActivityDestroyWatcher.install逻辑类似, 但复杂点
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> AppWatcher.Config
) {
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
//AndroidOFragmentDestroyWatcher
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
)
}
//AndroidXFragmentDestroyWatcher
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}
//AndroidSupportFragmentDestroyWatcher
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
fragmentDestroyWatchers.add(it)
}
if (fragmentDestroyWatchers.size == 0) {
return
}
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
watcher(activity)
}
}
})
}
类型判断, 分别交给AndroidOFragmentDestroyWatcher/AndroidXFragmentDestroyWatcher/AndroidSupportFragmentDestroyWatcher处理
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"
)
}
}
}
override fun invoke(activity: Activity) {
val fragmentManager = activity.fragmentManager
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
}
}
看到这里也许有点蒙圈, 简单来说, 就是监听Activity的销毁, 在Activity销毁的时候, 通过FragmentManager#registerFragmentLifecycleCallbacks监听Fragment的销毁, 在Fragment执行onFragmentDestroyed和onFragmentViewDestroyed的时候, 触发监听Fragment.
用代码表示就是:
application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
activity.getFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
super.onFragmentViewDestroyed(fm, f);
//监听f.getView()
}
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
super.onFragmentDestroyed(fm, f);
//监听fragment
}
});
}
});
监听的方法跟监听Activity一样是leakcanary.ObjectWatcher#watch
在分析leakcanary.ObjectWatcher#watch之前, 我们还有一个重要的方法得分析
leakcanary.internal.InternalAppWatcher#install方法的最后一行
onAppWatcherInstalled(application)
onAppWatcherInstalled是InternalLeakCanary
onAppWatcherInstalled(application)实际执行的是leakcanary.internal.InternalLeakCanary#invoke
leakcanary.internal.InternalLeakCanary#invoke
override fun invoke(application: Application) {
_application = application
checkRunningInDebuggableBuild()
//重要的是这里
//注册了监听
//回调方法是onObjectRetained
//后面会用到
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))
//构造GC工具
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
)
//...
}
leakcanary.ObjectWatcher#watch
@Synchronized fun watch(
watchedObject: Any,
description: String
) {
//...
//生成弱引用对象
//注意最后一个参数queue, 就是我们前面讲的ReferenceQueue
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
//...
watchedObjects[key] = reference
//在线程池执行
checkRetainedExecutor.execute {
moveToRetained(key)
}
}
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
//注意这里执行了onObjectRetained, 也就是上面leakcanary.internal.InternalLeakCanary#onObjectRetained方法
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
所以, 接下来我们看看leakcanary.internal.InternalLeakCanary#onObjectRetained
leakcanary.internal.InternalLeakCanary#onObjectRetained
override fun onObjectRetained() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.onObjectRetained()
}
}
查看调用链, 最后执行了leakcanary.internal.HeapDumpTrigger#checkRetainedObjects
!!!核心方法来了
private fun checkRetainedObjects(reason: String) {
//...
var retainedReferenceCount = objectWatcher.retainedObjectCount
//如果没被回收的对象数大于0, 就是仍然有对象没被回收
if (retainedReferenceCount > 0) {
//执行GC
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
//如果GC后都被回收了
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
//...
//一堆防止误判的逻辑
//生成heap文件
dumpHeap(retainedReferenceCount, retry = true)
}
leakcanary.GcTrigger.Default#runGc
override fun runGc() {
//执行gc
Runtime.getRuntime().gc()
enqueueReferences()
//强制调用已经失去引用的对象的finalize方法
System.runFinalization()
}
leakcanary.internal.HeapDumpTrigger#dumpHeap
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean
) {
//...生成heapDump文件
val heapDumpFile = heapDumper.dumpHeap()
//...开启Service分析heapDump文件
HeapAnalyzerService.runAnalysis(application, heapDumpFile)
}
小结:
- 监听时机: Activity执行onDestroy的时候
- 发现泄漏对象:WeakReference+ReferenceQueue, 触发GC, 没有出现在ReferenceQueue中的就是泄漏的对象.
- 分析泄漏对象:生成heapDumpFile文件, 调用HeapAnalyzerService分析该文件
下面我们就来看看, 如何生成heapDumpFile , 以及如何分析heapDumpFile .
在上面的leakcanary.internal.HeapDumpTrigger#dumpHeap方法中, 我们可以看到使用leakcanary.internal.HeapDumper#dumpHeap生成heapDumpFile, 而heapDumper是接口, 其具体实现是leakcanary.internal.AndroidHeapDumper, 所以我们就来看看leakcanary.internal.AndroidHeapDumper#dumpHeap
leakcanary.internal.AndroidHeapDumper#dumpHeap
override fun dumpHeap(): File? {
//...
return try {
//实际上就是调用了系统的android.os.Debug#dumpHprofData(java.lang.String)
Debug.dumpHprofData(heapDumpFile.absolutePath)
//...
}
}
我们可以看到, 生成heapDumpFile文件实际上是调用了系统的方法Debug#dumpHprofData, 参数是文件路径
知道了如何生成heapDumpFile, 我们来看最后一步, 分析heapDumpFile, 也即是, 找到泄漏对象的调用栈.
leakcanary.internal.HeapAnalyzerService
首先调用的就是leakcanary.internal.HeapAnalyzerService.Companion#runAnalysis, 启动Service
leakcanary.internal.HeapAnalyzerService.Companion#runAnalysis
fun runAnalysis(
context: Context,
heapDumpFile: File
) {
val intent = Intent(context, HeapAnalyzerService::class.java)
intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
startForegroundService(context, intent)
}
private fun startForegroundService(
context: Context,
intent: Intent
) {
if (SDK_INT >= 26) {
context.startForegroundService(intent)
} else {
// Pre-O behavior.
context.startService(intent)
}
}
方法很简单, 就是启动HeapAnalyzerService, 如果API大于26, 调用context.startForegroundService, 否则 context.startService
接下来, 我们看下HeapAnalyzerService
HeapAnalyzerService是IntentService的子类, IntentService自带线程(HandlerThread), 通过一系列封装, 执行Service#onStart和Service#onStartCommand时, 都会在子线程(HandlerThread)中执行HeapAnalyzerService#onHandleIntentInForeground
所以, 我们需要看的是HeapAnalyzerService#onHandleIntentInForeground
HeapAnalyzerService#onHandleIntentInForeground
override fun onHandleIntentInForeground(intent: Intent?) {
//设置线程优先级
// Since we're running in the main process we should be careful not to impact it.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
val heapDumpFile = intent.getSerializableExtra(HEAPDUMP_FILE_EXTRA) as File
val config = LeakCanary.config
val heapAnalysis = if (heapDumpFile.exists()) {
//开始分析heapDumpFile文件
analyzeHeap(heapDumpFile, config)
} else {
missingFileFailure(heapDumpFile)
}
onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
config.onHeapAnalyzedListener.onHeapAnalyzed(heapAnalysis)
}
analyzeHeap方法实际调用HeapAnalyzer#analyze
HeapAnalyzer#analyze
fun analyze(
heapDumpFile: File,
leakingObjectFinder: LeakingObjectFinder,
referenceMatchers: List = emptyList(),
computeRetainedHeapSize: Boolean = false,
objectInspectors: List = emptyList(),
metadataExtractor: MetadataExtractor = MetadataExtractor.NO_OP,
proguardMapping: ProguardMapping? = null
): HeapAnalysis {
//... 检查heapDumpFile文件是否存在, 不存在就不用分析了, 直接失败
return try {
listener.onAnalysisProgress(PARSING_HEAP_DUMP)
//
Hprof.open(heapDumpFile)
.use { hprof ->
val graph = HprofHeapGraph.indexHprof(hprof, proguardMapping)
val helpers =
FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors)
helpers.analyzeGraph(
metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime
)
}
} catch (exception: Throwable) {
//... 分析失败
}
}
Hprof.Companion#open
fun open(hprofFile: File): Hprof {
//... 简单描述就是通过okio读文件, 生成Hprof
return Hprof(
channel, source, reader, heapDumpTimestamp, hprofVersion, fileLength
)
}
HprofHeapGraph.Companion#indexHprof
HprofHeapGraph.Companion#indexHprof -> HprofInMemoryIndex.Companion#createReadingHprof -> HprofReader#readHprofRecords
在HprofReader#readHprofRecords中, 对文件进行了解析
HprofReader#readHprofRecords
fun readHprofRecords(
recordTypes: Set>,
listener: OnHprofRecordListener
) {
//...
//使用while对文件进行解析
//解析各个tag
//生成对应信息
while (!exhausted()) {
// type of the record
val tag = readUnsignedByte()
when (tag) {
STRING_IN_UTF8 -> {
if (readStringRecord) {
val recordPosition = position
val id = readId()
val stringLength = length - identifierByteSize
val string = readUtf8(stringLength)
val record = StringRecord(id, string)
listener.onHprofRecord(recordPosition, record)
} else {
skip(length)
}
}
//... 与STRING_IN_UTF8 同理, 同样是对各个tag进行分类解析
}
}
}
解析到文件数据以后, 开始对数据查找泄漏对象的调用栈信息. 生成各个调用栈信息并保存在classIndex/instanceIndex/objectArrayIndex/primitiveArrayIndex几个SortedBytesMap中
注意, 这里的listener是HprofInMemoryIndex.Builder
在HprofInMemoryIndex.Builder#onHprofRecord方法中, 可以看到, 其解析并保存了全部的GCRoot对象
override fun onHprofRecord(
position: Long,
record: HprofRecord
) {
when (record) {
//..其它类型对象
is GcRootRecord -> {
val gcRoot = record.gcRoot
if (gcRoot.id != ValueHolder.NULL_REFERENCE
&& indexedGcRootsTypes.contains(gcRoot.javaClass)
) {
gcRoots += gcRoot
}
}
//..其它类型对象
}
}
HeapAnalyzer#analyzeGraph
private fun FindLeakInput.analyzeGraph(
metadataExtractor: MetadataExtractor,
leakingObjectFinder: LeakingObjectFinder,
heapDumpFile: File,
analysisStartNanoTime: Long
): HeapAnalysisSuccess {
//...
//找到泄漏对象的id
//leakingObjectFinder是接口, 实际实现是FilteringLeakingObjectFinder
val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)
//构建泄漏对象的调用栈信息
val (applicationLeaks, libraryLeaks) = findLeaks(leakingObjectIds)
return HeapAnalysisSuccess(
heapDumpFile = heapDumpFile,
createdAtTimeMillis = System.currentTimeMillis(),
analysisDurationMillis = since(analysisStartNanoTime),
metadata = metadata,
applicationLeaks = applicationLeaks,
libraryLeaks = libraryLeaks
)
}
HeapAnalyzer#findLeaks
private fun FindLeakInput.findLeaks(leakingObjectIds: Set): Pair, List> {
val pathFinder = PathFinder(graph, listener, referenceMatchers)
val pathFindingResults =
pathFinder.findPathsFromGcRoots(leakingObjectIds, computeRetainedHeapSize)
//...
return buildLeakTraces(pathFindingResults)
}
shark.internal.PathFinder#findPathsFromGcRoots(shark.internal.PathFinder.State) -> shark.HprofHeapGraph#findObjectById -> shark.internal.HprofInMemoryIndex#indexedObjectOrNull
HprofInMemoryIndex#indexedObjectOrNull 的内容比较简单, 就是根据objectId从前面存的classIndex/instanceIndex/objectArrayIndex/primitiveArrayIndex中取数据, 能从哪个中取到就直接返回
最后一步就是shark.HeapAnalyzer#buildLeakTraces方法构建调用栈信息了.
总结
- 触发监听
Activity执行onDestroy时, 触发对Activity的监听.(android.app.Application#registerActivityLifecycleCallbacks)
Fragment执行onDestroyView时触发对Fragment的监听(FragmentManager#registerFragmentLifecycleCallbacks) - 发现泄漏对象
WeakReference+ReferenceQueue机制, 执行GC(GcTrigger.Default#runGc, 实际执行Runtime.getRuntime().gc())后, 如果有对象不在ReferenceQueue中, 就认为发生了泄漏 - 生成dumpHeap文件
(android.os.Debug#dumpHprofData(java.lang.String) - 解析文件(HeapAnalyzerService), 生成调用栈
其它
内存分析的实现机制这一块可以对比KOOM 以及 介绍