LeakCanary2源码分析
LeakCanary介绍
A memory leak detection library for Android.
LeakCanary是Android上用于检查内存泄漏的工具,LeakCanary大大减少因内存泄漏导致的内存溢出(OutOfMemoryError)奔溃,LeakCanary的易用和有效让它成为我最喜欢的开源框架之一,下面我们会先简单介绍其用法,然后深入了解LeakCanary检查内存泄漏的原理。
LeakCanary使用
加入依赖
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
}
只需要添加相关依赖,便可集成,非常方便,如果从1.6版本升级,可以参考《Upgrading to LeakCanary 2》进行升级。
安装并启动APP,看到下面这个log,说明已经添加成功:
D/LeakCanary: Installing AppWatcher
简单实践
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Log.d("MainActivity","Run");
}
}, 10000);
}
}
这里我们写了个10s的延时任务,打开APP后迅速按返回键退出,这个时候LeakCanary有如下提示:

从LeakCanary的提示我们可以知道,MainActivity发生了内存泄漏,而泄漏的原因是MessageQueue持有了MainActivity,导致MainActivity无法正常退出,这里只是简单举例,大家可以加到自己项目中,相信会有不少收获。
LeakCanary源码分析
本文使用的是LeakCanary最新的2.2版本
从1.6.3开始,LeakCanary就使用Kotlin重写了一次,代码量(不包括空格和注释)从Java时期的6000增加到Kotlin版本的16000行,代码增加的原因是LeakCanary不在依赖HAHA(perflib的重新打包),而是重新开发了自己的堆转储解析器:Shark,Shark的使用提升了速度并减少了内存使用。额外代码主要来自Shark, 自动化测试和权限相关的UI代码。
下面我们将开始分析全新的,Kotlin版本的LeakCnary:
1. 如何查看源码
因为我们使用的2.2版本,没有显式的框架入口,无法使用AndroidStudio的关联功能,所有我们要自己手动找代码,那么如何找到LeakCanary相关代码呢,我们需要到Lib里面找:
-
在Project Pannel中选择 “Project”
-
点击"External Libraries"
-
下拉找到LeakCanary的包,展开就可以看到相关代码

我们可以看到LeakCanary的不少包,主要包括:
-
leakcanary-android
集成入口模块,提供 LeakCanary 安装,公开 API 等能力
-
leakcanary-android-process
和 leakcanary-android 一样,区别是会在单独的进程进行分析
-
leakcanary-android-core
核心模块
-
leakcanary-object-watcher-android,leakcanary-object-watcher-android-androidx,leakcanary-watcher-android-support-fragments
对象实例观察模块,在
Activity,Fragment等对象的生命周期中,注册对指定对象实例的观察,有Activity,Fragment,Fragment View,ViewModel等 -
shark-android
提供特定于 Android 平台的分析能力。例如设备的信息,Android 版本,已知的内存泄露问题等
-
shark
hprof 文件解析与分析的入口模块
-
shark-graph
分析堆中对象的关系图模块
-
shark-hprof
解析 hprof 文件模块
-
shark-log
日志模块
2. 源码分析
LeakCanary 2.0不需要添加代码便可以跟随APP启动,原理在于利用了ContentProvider的特性,ContentProvider.onCreate方法会先于Application.onCreate执行。具体的实现只需要在AndroidManifeat.xml中配置一下定制的ContentProvider,在其onCreate方法中通过install进行初始化初始化,便可以省去LeakCanary 1.6版本中在Application中install的步骤:
//注册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>
首先在AndroidManifest.xml中注册contentProvider,然后对contentProvider进行定义:
//@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已经加载过,直接放回
return
}
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
//-->2.1监视Activity
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.2监视Fragment
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.3调用上层模块InternalLeakCanary.invoke
onAppWatcherInstalled(application)
}
2.1 ActivityDestroyWatcher.install
// @ActivityDestroyWatcher.kt
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
//-->2.1.1 创建Activity destroy监听回调
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
//-->2.1.2 同Application绑定
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
//2.1.1 创建Activity destroy监听回调
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
//Activity destroy触发存在对象检查
if (configProvider().watchActivities) {
// -->2.1.2 objectWatcher监视activity
objectWatcher.watch(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
}
//2.1.2 objectWatcher监视activity
@Synchronized fun watch(
watchedObject: Any, //activity
description: String
) {
if (!isEnabled()) {
return
}
//-->2.1.3将可达activity删除
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
//根据activity建对应的弱引用,并绑定ReferenceQueue
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
...
//将reference保存到watchedObjects数组中
watchedObjects[key] = reference
//-->2.1.4 启动延时5s任务
checkRetainedExecutor.execute {
//-->2.1.5 获取GC无法回收的Activity
moveToRetained(key)
}
}
//2.1.3将可达activity删除 @ObjectWatcher.kt
private fun removeWeaklyReachableObjects() {
var ref: KeyedWeakReference?
do {
//重点,在GC或者finalization之前,在WeakReferences的被引用对象(这里是Activity)的可达性更改时,
//会把WeakReferences添加到创建时候指定的ReferenceQueue队列,这些可达性变更得对象,就是内存不泄露对象
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
//在watchedObjects中删除不发送内存泄漏对象,剩下内存泄漏对象
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
//2.1.4 启动延时5s任务 @ObjectWatcher.kt
val watchDurationMillis: Long = TimeUnit.SECONDS.toMillis(5)
private val checkRetainedExecutor = Executor {
//在主线程延时五秒执行任务
mainHandler.postDelayed(it, AppWatcher.config.watchDurationMillis)
}
//2.1.5 获取GC无法回收的Activity @ObjectWatcher.kt
@Synchronized private fun moveToRetained(key: String) {
//-->2.1.3将可达activity删除
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
//保存当前时间作为泄漏时间
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
//-->2.1.6 通知InternalLeakCanary发生内存泄漏
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
//2.1.6 通知InternalLeakCanary发生内存泄漏 @HeapDumpTrigger.kt
override fun onObjectRetained() {
if (this::heapDumpTrigger.isInitialized) {
//-->2.1.7通知heapDumpTrigger有内存泄漏
heapDumpTrigger.onObjectRetained()
}
}
//2.1.7 通知heapDumpTrigger有内存泄漏 @HeapDumpTrigger.kt
fun onObjectRetained() {
//-->2.1.8 再次检查内存泄漏
scheduleRetainedObjectCheck(
reason = "found new object retained",
rescheduling = false
)
}
//2.1.8 再次检查内存泄漏 @HeapDumpTrigger.kt
private fun scheduleRetainedObjectCheck(...) {
...
backgroundHandler.postDelayed({
checkScheduledAt = 0
//-->2.1.9 检查泄漏对象
checkRetainedObjects(reason)
}, delayMillis)
}
//2.1.9 检查泄漏对象 @HeapDumpTrigger.kt
private fun checkRetainedObjects(reason: String) {
...
if (retainedReferenceCount > 0) {
//-->2.1.10 GC一次,确认所有存在对象都是泄漏对象
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
}
//-->2.1.11 检查当前存在对象的个数
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached) {
...
//如果配置了debug不使用heap且正在debug,延时20s在执行checkRetainedObjects(reason)
scheduleRetainedObjectCheck(
reason = "debugger is attached",
rescheduling = true,
//WAIT_FOR_DEBUG_MILLIS为20s
delayMillis = WAIT_FOR_DEBUG_MILLIS
)
return
}
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
//WAIT_BETWEEN_HEAP_DUMPS_MILLIS为60,60s内执行一次操作
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS) {
...
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
...
return
}
//-->2.1.12 执行dump Heap操作
dumpHeap(retainedReferenceCount, retry = true)
}
// 2.1.10 触发GC @GcTrigger.kt
object Default : GcTrigger {
override fun runGc() {
//System.gc() 并不会每次都引起GC. Runtime.gc()会更有效
Runtime.getRuntime()
.gc()
...
}
}
//2.1.11 检查当前所有存在对象个数 @HeapDumpTrigger.kt
private fun checkRetainedCount(
retainedKeysCount: Int,
retainedVisibleThreshold: Int
): Boolean {
val countChanged = lastDisplayedRetainedObjectCount != retainedKeysCount
lastDisplayedRetainedObjectCount = retainedKeysCount
if (retainedKeysCount == 0) {
//存在对象为0
return true
}
if (retainedKeysCount < retainedVisibleThreshold) {
//存在对象低于阈值5个
if (applicationVisible || applicationInvisibleLessThanWatchPeriod) {
...
//当前应用可见,或者不可见时间间隔少于 5s,重新安排到2s后执行checkRetainedObjects
scheduleRetainedObjectCheck(
reason = "found only $retainedKeysCount retained objects (< $retainedVisibleThreshold while app visible)",
rescheduling = true,
delayMillis = WAIT_FOR_OBJECT_THRESHOLD_MILLIS
)
return true
}
}
return false
}
在继续dumpHeap前,LeakCanary还做了如下操作:
- 首先获取存在对象的个数,如果大于0,则GC一次之后再次获取
- 如果步骤1后存在对象的个数小于5个,且应用可见,或者不可见时间小于5s,退出,定时2s后再次检查存在对象
- 如果步骤1后存在对象的个数大于等于5个,则继续执行下面的代码,准备dump heap
- 如果config里面配置的“调试时不允许dump heap”为false(默认值)且正在调试,退出,定时20s再次检查存在对象
- 如果DumpHeap的时间间隔不足60s,退出,并等间隔时间达到60s再次检查存在对象
- 如果函数没退出,开始dumpHeap
下面继续看dumpHeap的实现:
//2.1.12 执行dump Heap操作 @HeapDumpTrigger.kt
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean
) {
...
//-->2.1.13 生成Heap文件
val heapDumpFile = heapDumper.dumpHeap()
...
//存在的对象已经dumpHeap,故不需要保留
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
//-->2.4 分析Heap文件
HeapAnalyzerService.runAnalysis(application, heapDumpFile)
}
//2.1.13 生成Heap文件 @AndroidHeapDumper
override fun dumpHeap(): File? {
...
return try {
//使用sdk提供的dumpHprofData生成Heap文件
Debug.dumpHprofData(heapDumpFile.absolutePath)
...
}
}
至此,对Activity内存泄漏的监听注册到dump Heap过程源码分析完毕,接下来我们看看LeakCanary对Fragment是如何操作的。
2.2 FragmentDestroyWatcher.install
//2.2监视Fragment
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
我们继续看Fragment对应的install处理
fun install(...) {
//创建观察者集合
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
//如果SDK大于等于O,则添加"android.app.Fragment"的观察者AndroidOFragmentDestroyWatcher
if (SDK_INT >= O) {
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
)
}
//对"androidx.fragment.app.Fragment"添加观察
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
//-->2.2.1添加观察者AndroidXFragmentDestroyWatcher
fragmentDestroyWatchers.add(it)
}
//对"android.support.v4.app.Fragment"添加观察
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let {
//添加观察者AndroidSupportFragmentDestroyWatcher
fragmentDestroyWatchers.add(it)
}
//如果观察列表为空,直接返回
if (fragmentDestroyWatchers.size == 0) {
return
}
//对所有Fragment进行监视
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
) {
for (watcher in fragmentDestroyWatchers) {
//-->2.2.2触发观察者的invoke()方法
watcher(activity)
}
}
})
}
watch(activity) 实际是对应kotlin中Functions.kt文件的Function1接口,P1为Activity,R为Unit即为Java的void:
package kotlin.jvm.functions
interface Function1<in P1, out R> : kotlin.Function<R> {
fun invoke(p1: P1): R
}
也就是说,watch(activity)最终调用的是watcher中的invoke(Activity)方法
AndroidOFragmentDestroyWatcher、AndroidXFragmentDestroyWatcher和AndroidXFragmentDestroyWatcher源码非常类似,只是针对的Fragment不同而调用的API不同而已,下面以AndroidXFragmentDestroyWatcher为例看看里面是如何实现的。
//2.2.1添加观察 @AndroidXFragmentDestroyWatcher.kt
internal class AndroidXFragmentDestroyWatcher(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
) : (Activity) -> Unit {
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentCreated(
fm: FragmentManager,
fragment: Fragment,
savedInstanceState: Bundle?
) {
ViewModelClearedWatcher.install(fragment, objectWatcher, configProvider)
}
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
) {
val view = fragment.view
if (view != null && configProvider().watchFragmentViews) {
// -->2.1.2 objectWatcher监视view
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) {
// -->2.1.2 objectWatcher监视view
objectWatcher.watch(
fragment, "${fragment::class.java.name} received Fragment#onDestroy() callback"
)
}
}
}
//2.2.2触发观察者的invoke()方法
override fun invoke(activity: Activity) {
if (activity is FragmentActivity) {
val supportFragmentManager = activity.supportFragmentManager
//-->2.2.3注册监听fragment生命周期回调
supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
ViewModelClearedWatcher.install(activity, objectWatcher, configProvider)
}
}
}
Watcher向Activity的FragmentManager注册FragmentLifecycleCallbacks,这样在Fragment调用onDestroyView和onDestory之后就能观察Fragment的View或者Fragment本身是否存在泄漏。将Fragment进行监视之后的流程同Activity,可以参考2.1节的分析。
2.3 onAppWatcherInstalled
//2.3 @InternalAppWatcher.kt
init {
val internalLeakCanary = try {
val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
leakCanaryListener.getDeclaredField("INSTANCE")
.get(null)
} catch (ignored: Throwable) {
NoLeakCanary
}
@kotlin.Suppress("UNCHECKED_CAST")
onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
}
internalLeakCanary是一个方法对象,这个对象的值是通过反射获取的 InternalLeakCanary.INSTANCE ,它是一个单例对象。InternalLeakCanary 位于 leakcanary-android-core 模块,这也是需要反射的原因。
在install中调用onAppWatcherInstalled(application), 同2.2提到的watch(activity),实际是对应kotlin中Functions.kt文件的Function1接口,P1为Application,R为Unit即为Java的void:
也就是onAppWatcherInstalled(application) 实际调用的是InternalLeakCanary.invoke方法:
//@InternalLeakCanary.kt
override fun invoke(application: Application) {
this.application = application
//2.3.1 添加内存泄漏监听,方便objectWatcher通知InternalLeakCanary
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
//初始化heapDumper,用于生成heap文件
val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
//指定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 = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
configProvider
)
//监听application是否可见
application.registerVisibilityListener { applicationVisible ->
this.applicationVisible = applicationVisible
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
}
//监听Activity Resume
registerResumedActivityListener(application)
//创建动态快捷方式
addDynamicShortcut(application)
disableDumpHeapInTests()
}
基本上InternalLeakCanary.init将后续监视和生成Heap文件需要用到的文件进行了初始化,写在install之后方便大家都流程有更深刻的理解。
2.4 HeapAnalyzerService.runAnalysis
//2.4 分析Heap文件 @HeapDumpTrigger.kt
HeapAnalyzerService.runAnalysis(application, heapDumpFile)
下面我们对runAnalysis流程进行分析:
internal class HeapAnalyzerService : ForegroundService(
HeapAnalyzerService::class.java.simpleName,
R.string.leak_canary_notification_analysing,
R.id.leak_canary_notification_analyzing_heap
), OnAnalysisProgressListener {
override fun onHandleIntentInForeground(intent: Intent?) {
...
//-->2.4.1在service中分析Heap文件
analyzeHeap(heapDumpFile, config)
...
}
//2.4.1在service中分析Heap文件
private fun analyzeHeap(...): HeapAnalysis {
val heapAnalyzer = HeapAnalyzer(this)
//对混淆的代码进行支持
val proguardMappingReader = try {
ProguardMappingReader(assets.open(PROGUARD_MAPPING_FILE_NAME))
} catch (e: IOException) {
null
}
//-->2.4.2 heapAnalyzer对Heap文件进行分析
return heapAnalyzer.analyze(
heapDumpFile = heapDumpFile,
leakingObjectFinder = config.leakingObjectFinder,
referenceMatchers = config.referenceMatchers,
computeRetainedHeapSize = config.computeRetainedHeapSize,
objectInspectors = config.objectInspectors,
metadataExtractor = config.metadataExtractor,
proguardMapping = proguardMappingReader?.readProguardMapping()
)
}
companion object {
...
fun runAnalysis(... ) {
val intent = Intent(context, HeapAnalyzerService::class.java)
intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
startForegroundService(context, intent)
}
private fun startForegroundService(
context: Context,
intent: Intent
) {
//在Android O之后 使用前台服务进行分析
if (SDK_INT >= 26) {
context.startForegroundService(intent)
} else {
//在Android O之前 使用后台服务进行分析
context.startService(intent)
}
}
}
}
//2.4.2 heapAnalyzer对Heap文件进行分析 @HeapAnalyzer.kt
fun analyze(...): HeapAnalysis {
...
return try {
listener.onAnalysisProgress(PARSING_HEAP_DUMP)
Hprof.open(heapDumpFile)
.use { hprof ->
//生成heap graph, 用于表示heap中的对象关系图
val graph = HprofHeapGraph.indexHprof(hprof, proguardMapping)
//初始化FindLeakInput
val helpers =
FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors)
//-->2.4.3 分析heap graph
helpers.analyzeGraph(
metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime
)
}
}
...
}
//2.4.3 分析heap graph @HeapAnalyzer.kt
private fun FindLeakInput.analyzeGraph(...): HeapAnalysisSuccess {
listener.onAnalysisProgress(EXTRACTING_METADATA)
//提取graph的metadata数据
val metadata = metadataExtractor.extractMetadata(graph)
listener.onAnalysisProgress(FINDING_RETAINED_OBJECTS)
//获取泄露对象的 objectIds
val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)
//-->2.4.4 生成泄露对象报告
val (applicationLeaks, libraryLeaks) = findLeaks(leakingObjectIds)
return HeapAnalysisSuccess(...)
}
//2.4.4 生成泄露对象报告
//共有两种泄漏类型:ApplicationLeak 和 LibraryLeak
private fun FindLeakInput.findLeaks(leakingObjectIds: Set<Long>): Pair<List<ApplicationLeak>, List<LibraryLeak>> {
val pathFinder = PathFinder(graph, listener, referenceMatchers)
//查询泄露对象到 GC Roots 的路径
val pathFindingResults =
pathFinder.findPathsFromGcRoots(leakingObjectIds, computeRetainedHeapSize)
return buildLeakTraces(pathFindingResults)
}
内存分析设计到LeakCanary自身设计的Shark架构,相关信息可以参考官网
小结
通过阅读源码,我们可以看到LeakCanary的核心逻辑在于利用ReferenceQueue对WeakReferences可达性改变进行监听,获取发送内存泄漏的对象,同时使用自己的Shark对Heap文件进行分析,进而为使用者提供了一个可视化的内存优化神器,我们也可以看到做了对于是否为为内存泄漏的判断十分严谨,又是GC又是延时。
在明白获取内存泄漏信息机制的同时,我们也明白,该机制只对Java层起作用,而Native层并不在检查范围。
参考

本文详细介绍了LeakCanary2在Android上的应用,包括其内存泄漏检测的原理,从添加依赖、简单实践到源码分析。重点讲解了ActivityDestroyWatcher和FragmentDestroyWatcher的安装过程,以及HeapAnalyzerService如何进行内存分析。通过对源码的深入理解,展示了LeakCanary2如何帮助开发者发现并解决内存泄漏问题。

686

被折叠的 条评论
为什么被折叠?



