LeakCanary是Android上用于檢查內(nèi)存泄漏的工具丈咐,LeakCanary大大減少因內(nèi)存泄漏導致的內(nèi)存溢出(OutOfMemoryError)崩潰凌彬。
從1.6.3開始小槐,LeakCanary就使用Kotlin重寫了一次辐马,這里的的源碼來自于版本2.5转砖,加入引用:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
LeakCanary包結構
leakcanary-android
集成入口模塊,提供 LeakCanary 安裝哀卫,公開 API 等能力
leakcanary-android-process
和 leakcanary-android 一樣丽涩,區(qū)別是會在單獨的進程進行分析
leakcanary-android-core
核心模塊
leakcanary-object-watcher-android萄唇,leakcanary-object-watcher-android-androidx,leakcanary-watcher-android-support-fragments
對象實例觀察模塊屎勘,在 Activity侄柔,F(xiàn)ragment 等對象的生命周期中共啃,注冊對指定對象實例的觀察,有 Activity暂题,F(xiàn)ragment移剪,F(xiàn)ragment View,ViewModel 等
shark-android
提供特定于 Android 平臺的分析能力薪者。例如設備的信息纵苛,Android 版本,已知的內(nèi)存泄露問題等
shark
hprof 文件解析與分析的入口模塊
shark-graph
分析堆中對象的關系圖模塊
shark-hprof
解析 hprof 文件模塊
shark-log
日志模塊
初始化
LeakCanary 2.0不需要添加代碼便可以跟隨APP啟動啸胧,省去了1.6版本前需要install的代碼赶站。原理在于利用了ContentProvider的特性,ContentProvider.onCreate方法會先于Application.onCreate執(zhí)行纺念。
leakcanary庫中聲明的ContentProvider贝椿。
//注冊ContentProvider @leakcanary-object-watcher-android/src/main/AndroidManifest.xml
<application>
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:exported="false"/>
</application>
//@AppWatcherInstaller.kt
internal class LeakCanaryProcess : AppWatcherInstaller() {
override fun onCreate(): Boolean {
super.onCreate()
AppWatcher.config = AppWatcher.config.copy(enabled = false)
return true
}
}
override fun onCreate(): Boolean {
//獲取application
val application = context!!.applicationContext as Application
//-->2.1 加載LeakCanary
InternalAppWatcher.install(application)
return true
}
}
//2.1 加載LeakCanary @InternalAppWatcher.kt
fun install(application: Application) {
...
//檢查當前線程是否有主線程
checkMainThread()
if (this::application.isInitialized) {
//如果LeakCanary已經(jīng)加載過,直接放回
return
}
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
//-->2.1監(jiān)視Activity
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.2監(jiān)視Fragment
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.3調(diào)用上層模塊InternalLeakCanary.invoke
onAppWatcherInstalled(application)
}
監(jiān)聽器利用了Activity陷谱、fragment的生命周期回調(diào)烙博,在ActivityDestroyWatcher類中瑟蜈,獲取該銷毀的activity,添加了該activity的監(jiān)聽渣窜。
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
//-->2.1.1 創(chuàng)建Activity destroy監(jiān)聽回調(diào)
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
//-->2.1.2 同Application綁定
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
//2.1.1 創(chuàng)建Activity destroy監(jiān)聽回調(diào)
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
//Activity destroy觸發(fā)存在對象檢查
if (configProvider().watchActivities) {
// -->2.1.2 objectWatcher監(jiān)視activity
objectWatcher.watch(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
}
lifecycleCallbacks監(jiān)聽Activity的onDestroy方法铺根,正常情況下activity在onDestroy后需要立即被回收,onActivityDestroyed方法最終會調(diào)用RefWatcher.watch方法:
@Synchronized fun watch(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
//根據(jù)activity創(chuàng)建對應的弱引用乔宿,并綁定ReferenceQueue
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"
}
//將reference保存到watchedObjects數(shù)組中
watchedObjects[key] = reference
//啟動延時5s任務
checkRetainedExecutor.execute {
//獲取GC無法回收的Activity
moveToRetained(key)
}
}
監(jiān)測機制利用了Java的WeakReference和ReferenceQueue位迂,通過將Activity包裝到WeakReference中,被WeakReference包裝過的Activity對象如果被回收详瑞,該WeakReference引用會被放到ReferenceQueue中掂林,通過監(jiān)測ReferenceQueue里面的內(nèi)容就能檢查到Activity是否能夠被回收。
ReferenceQueue:
引用隊列坝橡,換言之就是存放引用的隊列泻帮,保存的是Reference對象。其作用在于Reference對象所引用的對象被GC回收時计寇,該Reference對象將會被加入引用隊列的隊尾锣杂。
//獲取GC無法回收的Activity
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
//保存當前時間作為泄漏時間
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
//通知InternalLeakCanary發(fā)生內(nèi)存泄漏
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中刪除不發(fā)送內(nèi)存泄漏對象,剩下內(nèi)存泄漏對象
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
//通知InternalLeakCanary發(fā)生內(nèi)存泄漏 @HeapDumpTrigger.kt
override fun onObjectRetained() {
if (this::heapDumpTrigger.isInitialized) {
//通知heapDumpTrigger有內(nèi)存泄漏
heapDumpTrigger.onObjectRetained()
}
}
-
LeakCanary檢測內(nèi)存泄漏的基本流程
1番宁、 首先通過removeWeaklyReachablereference來移除已經(jīng)被回收的Activity引用
2元莫、 通過gone(reference)判斷當前弱引用對應的Activity是否已經(jīng)被回收,如果已經(jīng)回收說明activity能夠被GC贝淤,直接返回即可柒竞。
3、 如果Activity沒有被回收播聪,調(diào)用GcTigger.runGc方法運行GC,GC完成后在運行第1步布隔,然后運行第2步判斷Activity是否被回收了离陶,如果這時候還沒有被回收,那就說明Activity可能已經(jīng)泄露衅檀。
4招刨、 如果Activity泄露了,就抓取內(nèi)存dump文件(Debug.dumpHprofData)
5哀军、 之后通過HeapAnalyzerService.runAnalysis進行分析內(nèi)存文件分析
接著通過HeapAnalyzer(checkForLeak—findLeakingReference---findLeakTrace)來進行內(nèi)存泄漏分析沉眶。
6、 最后通過DisplayLeakService進行內(nèi)存泄漏的展示杉适。