内存泄露(Memory Leak)是指程序中某些对象的内存得不到释放,即使这些对象已经不再被使用,但由于仍然有引用指向它们,垃圾回收器(GC)无法回收这些内存,从而导致内存占用越来越高,最终可能导致应用崩溃。
通过一系列称为“GC Roots”的对象作为起始点,从这些节点向下搜索,搜索所有的引用链,当一个对象到GC Roots 没有任何引用链(即GC Roots到对象不可达)时,则证明此对象是不可用的.
1. 内存泄露的常见原因
(1) 静态引用持有对象
-
静态变量的生命周期与应用进程一致,如果静态变量引用了某个长生命周期对象,就会导致该对象无法被回收。
示例:
kotlin 复制代码 object Singleton { var context: Context? = null }如果
Singleton.context持有Activity的引用,即使Activity被销毁,它的内存也无法释放。
(2) Handler 导致的泄露
-
当使用匿名或非静态内部类定义
Handler时,它会隐式持有外部类的引用(通常是Activity或Fragment)。 -
如果消息处理队列中仍有未处理的消息,即使
Activity被销毁,引用关系仍然存在,导致内存泄露。示例:
kotlin 复制代码 private val handler = Handler(Looper.getMainLooper()) { // 操作与 Activity 相关的逻辑 true }
(3) 监听器未解绑
-
在生命周期中注册的监听器(如广播接收器、观察者模式等)没有在合适的时机解除绑定。
示例:
kotlin 复制代码 LocalBroadcastManager.getInstance(context).registerReceiver(receiver, intentFilter) // 忘记调用 unregisterReceiver
(4) 内部类或匿名类
-
非静态内部类和匿名类会隐式持有外部类的引用,导致外部类的生命周期延长。
示例:
kotlin 复制代码 class ExampleActivity : AppCompatActivity() { private val runnable = Runnable { // 持有外部 Activity 的引用 } }
(5) WebView 导致的泄露
- WebView 持有大量资源(如缓存、JS 引擎)。如果没有正确清理,WebView 的内存无法释放。
(6) Bitmap 和 Drawable
-
大型图片对象未及时回收,或者被
ImageView等控件引用,导致内存占用增加。示例:
kotlin 复制代码 imageView.setImageBitmap(bitmap) // Activity 销毁后,bitmap 仍被引用,无法释放
2. 检测内存泄露的方法
(1) LeakCanary
-
一款广泛使用的 Android 内存泄露检测工具。
-
在 Debug 模式下使用,能自动检测内存泄露并生成报告。
集成方式:
groovy 复制代码 implementation 'com.squareup.leakcanary:leakcanary-android:2.x'
(2) Android Studio Profiler
- 使用 Memory Profiler 查看内存分配和对象引用。
- 可以手动触发垃圾回收,检查泄露对象是否存在。
(3) Debug API
- 使用
Debug.dumpHprofData()导出内存堆文件(.hprof),通过工具(如 MAT,Memory Analyzer Tool)分析内存泄露。
3. 解决内存泄露的策略
(1) 避免静态引用 Context 或 Activity
-
如果必须使用
Context,尽量使用Application的Context,因为它的生命周期与应用一致。错误示例:
kotlin 复制代码 object Singleton { var context: Context? = null }正确示例:
kotlin 复制代码 object Singleton { lateinit var context: Context private set fun init(applicationContext: Context) { context = applicationContext } }
(2) 静态内部类处理 Handler
-
将
Handler定义为静态内部类,并持有WeakReference指向外部类,避免隐式引用。示例:
kotlin 复制代码 private class MyHandler(activity: ExampleActivity) : Handler(Looper.getMainLooper()) { private val activityRef = WeakReference(activity) override fun handleMessage(msg: Message) { val activity = activityRef.get() ?: return // 操作 activity } }
(3) 生命周期中解绑监听器
-
在
onDestroy或onStop中解绑监听器。示例:
kotlin 复制代码 override fun onDestroy() { LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver) super.onDestroy() }
(4) WebView 的销毁
-
在
Activity销毁时,正确释放 WebView。示例:
kotlin 复制代码 override fun onDestroy() { webView.apply { loadUrl("about:blank") clearHistory() removeAllViews() destroy() } super.onDestroy() }
(5) 及时清理 Bitmap 和 Drawable
-
使用完 Bitmap 或 Drawable 后,及时调用
recycle()或解除引用。示例:
kotlin 复制代码 override fun onDestroy() { imageView.setImageDrawable(null) super.onDestroy() }
4. 内存泄露的危害
- 内存占用增加:无法释放的对象长期占用内存,最终导致内存不足。
- 性能下降:应用运行缓慢,GC 频率增加。
- 应用崩溃:因内存耗尽触发 OOM。
5. 总结
- 预防关键:关注对象生命周期,避免无意的长时间引用。
- 检测手段:使用 LeakCanary、Memory Profiler 等工具及时发现问题。
- 优化实践:使用弱引用(
WeakReference)、解绑监听器、清理大型资源,确保内存及时回收。
及时发现和解决内存泄露问题,对提升应用性能和稳定性至关重要。