一句话说透Android里面的内存泄漏,怎么查找,怎么产生的

143 阅读3分钟

一句话总结:
内存泄漏就是“垃圾赖着不走”,本该被回收的对象被其他对象“强行挽留”,导致内存占用越来越高,最终应用卡死或崩溃!


一、内存泄漏的常见场景(为什么会赖着不走?)

1. 匿名内部类(比如 Handler、Runnable)

  • 问题代码

    class MyActivity : AppCompatActivity() {  
        private val handler = Handler(Looper.getMainLooper()) {  
            // 处理消息(隐式持有 Activity 的引用)  
            true  
        }  
    
        override fun onCreate(savedInstanceState: Bundle?) {  
            super.onCreate(savedInstanceState)  
            // 发送延迟消息  
            handler.postDelayed({  
                updateUI()  
            }, 100000) // 延迟 100 秒  
        }  
    }  
    
  • 原因Handler 匿名内部类隐式持有 MyActivity 的引用,若在 Activity 销毁前未移除消息,Activity 无法被回收。

2. 单例模式持有 Context

  • 问题代码

    object SingletonManager {  
        private var context: Context? = null  
    
        fun init(context: Context) {  
            this.context = context // 传入了 Activity 的 Context  
        }  
    }  
    
  • 原因:单例的生命周期与应用一致,若持有 Activity 的 ContextActivity 销毁后无法释放。

3. 静态变量引用 View 或 Activity

  • 问题代码

    class MyActivity : AppCompatActivity() {  
        companion object {  
            var staticView: View? = null // 静态变量持有 View  
        }  
    
        override fun onCreate(savedInstanceState: Bundle?) {  
            super.onCreate(savedInstanceState)  
            staticView = findViewById(R.id.my_view)  
        }  
    }  
    
  • 原因:静态变量生命周期过长,导致 View 关联的 Activity 无法回收。

4. 未反注册监听器(如广播、回调)

  • 问题代码

    class MyActivity : AppCompatActivity() {  
        private val receiver = object : BroadcastReceiver() {  
            override fun onReceive(context: Context?, intent: Intent?) {  
                // 处理广播  
            }  
        }  
    
        override fun onCreate(savedInstanceState: Bundle?) {  
            super.onCreate(savedInstanceState)  
            registerReceiver(receiver, IntentFilter("MY_ACTION"))  
        }  
    
        // 忘记在 onDestroy 中反注册!  
    }  
    
  • 原因BroadcastReceiver 注册后未反注册,系统会一直持有 Activity 的引用。


二、如何查找内存泄漏?(抓赖账的垃圾)

1. 使用 LeakCanary(自动检测神器)

  • 步骤

    1. 添加依赖:

      dependencies {  
          debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'  
      }  
      
    2. 无需额外代码,应用运行后,LeakCanary 自动检测泄漏并弹出通知。

    3. 查看报告,定位泄漏对象和引用链。

2. 使用 Android Studio Profiler(手动分析)

  • 步骤

    1. 点击 Android Studio 底部的 Profiler → 选择 Memory
    2. 操作应用进入怀疑泄漏的页面,然后返回。
    3. 点击 Dump Java Heap 生成堆转储文件。
    4. 在 Heap Dump 中搜索疑似泄漏的类(如 Activity),查看引用路径。

3. 手动排查(土法炼钢)

  • 检查点

    • 所有 HandlerRunnable 是否在 onDestroy 中移除回调。
    • 单例、静态变量是否持有 Context 或 View
    • 监听器、广播、服务是否反注册。

三、如何解决内存泄漏?(赶走赖账的垃圾)

1. 匿名内部类改用静态内部类 + 弱引用

  • 修复代码

    class MyActivity : AppCompatActivity() {  
        private val handler = MyHandler(this)  
    
        // 静态内部类,不隐式持有 Activity  
        private class MyHandler(activity: MyActivity) : Handler(Looper.getMainLooper()) {  
            private val weakActivity = WeakReference(activity)  
    
            override fun handleMessage(msg: Message) {  
                weakActivity.get()?.updateUI()  
            }  
        }  
    
        override fun onDestroy() {  
            super.onDestroy()  
            handler.removeCallbacksAndMessages(null) // 移除所有消息  
        }  
    }  
    

2. 单例模式使用 Application Context

  • 修复代码

    object SingletonManager {  
        private var context: Context? = null  
    
        // 传入 Application Context  
        fun init(context: Context) {  
            this.context = context.applicationContext  
        }  
    }  
    

3. 静态变量避免引用 View/Activity

  • 修复代码

    class MyActivity : AppCompatActivity() {  
        // 改用弱引用  
        companion object {  
            private var weakView: WeakReference<View>? = null  
        }  
    
        override fun onCreate(savedInstanceState: Bundle?) {  
            super.onCreate(savedInstanceState)  
            weakView = WeakReference(findViewById(R.id.my_view))  
        }  
    }  
    

4. 及时反注册监听器

  • 修复代码

    class MyActivity : AppCompatActivity() {  
        private val receiver = object : BroadcastReceiver() {  
            override fun onReceive(context: Context?, intent: Intent?) {  
                // 处理广播  
            }  
        }  
    
        override fun onCreate(savedInstanceState: Bundle?) {  
            super.onCreate(savedInstanceState)  
            registerReceiver(receiver, IntentFilter("MY_ACTION"))  
        }  
    
        override fun onDestroy() {  
            super.onDestroy()  
            unregisterReceiver(receiver) // 反注册  
        }  
    }  
    

四、总结口诀

“内存泄漏要警惕,Handler 单例和静态,
匿名内部类是大坑,监听反注册别忘记。
LeakCanary 来检测,Profiler 抓堆快分析,
弱引用加及时清,内存优化稳如基!