非静态内部类导致activity内存泄漏,核心是其隐式强引用外部类且生命周期超期:handler延时消息、runnable后台线程未结束或监听器未注销时,activity无法被gc回收。

非静态内部类导致的 Activity/Context 泄漏,核心在于“隐式强引用”和“生命周期错配”。只要内部类实例存活时间超过外部 Activity,且它又没被及时清理,泄漏就发生了。
看内部类是否天然持有外部类引用
Java 规定:非静态内部类(包括匿名类、lambda 表达式在某些场景下)会自动持有一个指向外部类实例的隐式引用(即 this$0 字段)。这个引用是强引用,无法被 GC 回收。
常见典型有:
- Activity 中定义的非静态 Handler、Runnable、AsyncTask
- 直接 new 出来的匿名 View.OnClickListener、TextWatcher
- 未声明为 static 的自定义内部类,比如
class MyLoader extends AsyncTask
看该内部类是否可能比 Activity 活得更久
即使持有引用,也不一定泄漏——关键看它有没有机会“超期服役”。以下情况风险极高:
- Handler 发送了 延时消息(
postDelayed、sendMessageDelayed),Activity 已 finish,消息还在 MessageQueue 里排队 - Runnable 或 AsyncTask 启动了后台线程,任务尚未结束,Activity 却已销毁
- 注册了监听器(如 SensorManager、ContentObserver),但没在
onDestroy()或onStop()中注销,回调仍可能触发
看堆栈或引用链能否追踪到 Activity 实例
用 Android Profiler 或 LeakCanary 检测时,如果看到类似这样的引用路径:
Android 开发调试技能,通过系统 ADB 工具操作 Android 设备。以下场景必须触发此技能:(1) 直接 ADB 操作——安装 APK、查看设备列表、抓取 logcat 日志、查看已安装应用、清除应用数据、截图、重启设备、拉取/推送文件、查看 CPU/内存/电池信息、adb shell 操作;(2)...
MyActivity → MyHandler → Message → target(=MyHandler) → this$0(=MyActivity)
或
MyActivity → OnClickListener$1 → this$0(=MyActivity)
就说明非静态内部类正在把 Activity “钉”在内存里。这种链路清晰、层级明确,是判断该类泄漏的直接证据。
Web 开发中类似问题的对照理解
Web 端虽无“Activity”概念,但存在等价场景:
- React 组件中定义的事件回调(如
useEffect里的定时器、addEventListener)若未清除,会持续持有组件闭包中的 state 和 props,阻止组件卸载后被回收 - Vue 的
mounted中启动的 setInterval,未在beforeUnmount清除,也会让组件实例无法释放 - 这些本质上都是“闭包捕获了外层作用域引用 + 异步任务未终止”,逻辑与 Android 非静态内部类泄漏完全对应










