内存泄漏检测
问题
Android 中常见的内存泄漏场景有哪些?如何检测?
答案
常见泄漏场景
| 场景 | 原因 | 解决方案 |
|---|---|---|
| Activity 被静态引用 | 静态变量持有 Activity | 使用 WeakReference 或 Application Context |
| Handler 泄漏 | 非静态内部类持有外部 Activity 引用 | 使用静态内部类 + WeakReference |
| 匿名内部类/Lambda | 持有外部类引用 | 注意 Listener 的生命周期 |
| 单例持有 Activity | 单例中缓存了 Activity Context | 使用 Application Context |
| 未取消的协程 | 协程作用域未随生命周期取消 | 使用 lifecycleScope / viewModelScope |
| 未注销的监听器 | BroadcastReceiver/Callback 未注销 | 在 onDestroy 中取消 |
| WebView 泄漏 | WebView 持有 Activity 引用 | 动态添加/独立进程 |
经典 Handler 泄漏
// ❌ 内存泄漏:非静态内部类持有 Activity 引用
class MyActivity : AppCompatActivity() {
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
updateUI() // 隐式引用 Activity
}
}
}
// ✅ 修复:静态内部类 + WeakReference
class MyActivity : AppCompatActivity() {
private class SafeHandler(activity: MyActivity) : Handler(Looper.getMainLooper()) {
private val activityRef = WeakReference(activity)
override fun handleMessage(msg: Message) {
activityRef.get()?.updateUI()
}
}
// ✅✅ 更好的方案:直接用协程
private fun delayedUpdate() {
lifecycleScope.launch {
delay(5000)
updateUI() // lifecycleScope 在 onDestroy 自动取消
}
}
}
LeakCanary
build.gradle.kts
dependencies {
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
}
零配置,自动检测 Activity、Fragment、ViewModel、Service 和 View 的泄漏。泄漏时通知栏弹出提示,展示引用链。
LeakCanary 原理
常见面试问题
Q1: LeakCanary 是如何检测 Activity 泄漏的?
答案:
- 注册
ActivityLifecycleCallbacks监听onActivityDestroyed - 将 Activity 包装为
WeakReference,关联ReferenceQueue - 等待 5 秒后检查
ReferenceQueue,如果 WeakReference 未入队,说明 Activity 未被 GC 回收 - 触发手动 GC 再次检查
- 仍未回收则抓取 Heap Dump,使用 Shark 库解析引用链
Q2: 如何修复 WebView 内存泄漏?
答案:
// 方案 1:动态创建 WebView,使用 Application Context
val webView = WebView(applicationContext)
container.addView(webView)
override fun onDestroy() {
container.removeView(webView)
webView.stopLoading()
webView.destroy()
super.onDestroy()
}
// 方案 2:在独立进程中运行 WebView(彻底隔离)
// AndroidManifest.xml
// <activity android:name=".WebViewActivity" android:process=":webview" />
Q3: Kotlin 协程会导致内存泄漏吗?
答案:
如果使用 GlobalScope 或自定义 CoroutineScope 且未正确取消,会导致泄漏。推荐:
- Activity/Fragment 中用
lifecycleScope - ViewModel 中用
viewModelScope - 它们都会在组件销毁时自动取消