Android 内存泄漏 -> ViewModel持有Activity/Fragment导致的内存泄漏

问题一:ViewModel 持有构造它的 Activity/Fragment,会导致内存泄漏吗?

答案:是的,一定会导致内存泄漏(除非你手动清空,但那很难维护)。为什么?

这依然是 生命周期不对等 造成的。

  • 引用的本质:当你把 Activity 的 this 传给 ViewModel 时,ViewModel 内部就有了一个指向该 Activity 内存地址的指针(强引用)。
  • 存活时间差异:
    • Activity:是"短命"的。屏幕旋转、系统语言切换、分屏模式、或者系统内存不足时,它都会被销毁(onDestroy)。
    • ViewModel:是"长命"的。它的设计目的就是为了在 Activity 重建期间存活下来。只有当 Activity 彻底退出(按返回键退出或调用 finish())时,ViewModel 才会清除。
  • 场景推演(旋转屏幕):
    Activity A (实例1) 创建了 ViewModel,并将 this (实例1) 传给它。用户旋转屏幕。系统销毁 Activity A (实例1)。
  • 关键点来了:系统本该回收 (GC) 实例1 的内存。但是,ViewModel 还活着,并且手里死死抓着 实例1 的引用。
    结果:GC 无法回收 实例1。系统创建了 Activity A (实例2),并复用了同一个 ViewModel。此时,内存里有:正在显示的 实例2 + 无法回收的僵尸 实例1(包含它的 View、图片等)。这就构成了标准的内存泄漏。

源码分析

核心逻辑:ViewModel 被存储在 ViewModelStore 中,而 ViewModelStore 的生命周期 长于 Activity 的配置重建(如旋转)。
Activity 销毁时: 系统调用 onRetainNonConfigurationInstance()。这里会把 mViewModelStore 保存起来,不销毁。

java 复制代码
// ComponentActivity.java
// 1. Activity 销毁时:系统特意把 ViewModelStore 存起来
public final Object onRetainNonConfigurationInstance() {
    // ...
    NonConfigurationInstances nci = new NonConfigurationInstances();
    // 【核心罪魁祸首】:把包含 ViewModel 的 Store 保存到 nci 对象中
    nci.viewModelStore = mViewModelStore; 
    return nci; // 返回给系统进程保存,不随 Activity 销毁
}

Activity 重建时: 在 onCreate 中,系统把之前保存的 ViewModelStore 又还给了新 Activity。

kotlin 复制代码
// ComponentActivity.java
// 2. 新 Activity 重建时:把 ViewModelStore 取回来
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        // 【恢复】:新 Activity 拿到了旧的 ViewModelStore
        // 此时 ViewModel 依然活着,它手里还抓着 "旧 Activity" 的引用 -> 泄漏
        mViewModelStore = nc.viewModelStore;
    }
}

结论:

  • 流程:Activity A (销毁) -> ViewModelStore (存活) -> Activity A (重建)。
  • 泄漏点:如果 ViewModel 内部持有了 Activity A 的引用(this),因为 ViewModel 在 ViewModelStore 里没死,所以 Activity A 也死不了 -> 内存泄漏。
相关推荐
星夜离尘566 小时前
MATLAB 工程实验:不等间隔流量积分,先把缺口与覆盖率说清楚
开发语言·matlab·时间戳·数据质量·数值积分
_Evan_Yao6 小时前
Redis 缓存穿透、击穿、雪崩,一次讲清!
java·spring·mybatis
Ai-_Man7 小时前
Meta Muse还是不可以导出左侧所有对话,只能点进去单条内容导出,方法不对吗?
开发语言·人工智能·ai·小程序·c#·1024程序员节
梓豪csd7 小时前
消息订阅中心:谁该收到哪条通知
java·开发语言·人工智能·低代码·自动化·1024程序员节
秋饼7 小时前
Spring AI 2.1 Message Parts 深度实战:推理签名回放与跨轮记忆持久化的源码级落地
java·ai·技术分享·后端开发
学逆向的7 小时前
C 枚举变量
c语言·开发语言·网络安全
Sakura_j_h_8 小时前
Python 在定时任务里输出 emoji,UnicodeEncodeError 从哪来
开发语言·字符编码·windows·python·定时任务
thefool1122668 小时前
TCP 1
java·1024程序员节
美狐美颜SDK开放平台9 小时前
准备开发直播APP?视频美颜SDK接入前的十项检查清单
android·ios·音视频·直播美颜sdk·视频美颜sdk
传奇开心果编程9 小时前
【Jetpack Compose娓娓道来】第26课:Repository模式与数据层设计——数据的唯一入口
android·移动开发·1024程序员节·jetpack compose·编程语言理论·编程框架·声明式ui