调用 finish() 不会清除对 Activity 的内存引用

Posted

技术标签:

【中文标题】调用 finish() 不会清除对 Activity 的内存引用【英文标题】:Calling finish() does not clear memory references to Activity 【发布时间】:2013-01-04 21:30:50 【问题描述】:

在我的应用程序的简化版本中,我有两个活动,A 和 B。活动 A 启动 B,在完成一些工作后 B 调用完成()。在大多数设备(运行 4.2 的 Galaxy Nexus、运行 4.0.4 的 Droid 4 和运行 2.3.4 的 Droid 2)上使用内存分析器工具显示没有活动 B 的痕迹,这正是我的预期。

但是在运行 4.1.1 的三星 S3 上,MAT 显示活动 B 对象仍然存在,这是由于到以下 GC 根的路径(不包括弱/软引用):

Class Name                                                        | Shallow Heap | Retained Heap
-------------------------------------------------------------------------------------------------
com.myCo.myApp.ActivityB  @ 0x42720818                            |          264 |         3,280
|- <Java Local> java.lang.Thread @ 0x4271cf60  Thread-21941 Thread|           80 |        52,264
|- mOuterContext android.app.ContextImpl @ 0x426adf68             |          104 |           784
    |  '- mContext android.media.AudioManager @ 0x428e49a0        |           48 |           152
-------------------------------------------------------------------------------------------------

每次我启动和停止活动 B 时,MAT 都会显示活动 B 的内存占用的另一个实例。我打开/关闭活动 B 的次数越多,logcat 中报告的内存占用就越大。通过 MAT 强制 GC 不会删除对活动 B 内存的引用。

我有三个问题。

    为什么不同设备的内存/GC行为不同?

    在 S3 上,操作系统最终会回来并 GC 搁浅的活动 B 对象(换句话说,我应该不用担心,因为 Android 会在崩溃前清理它)吗?

    李>

    如果不是,Thread 和 AudioManager 引用来自哪里,我将如何清除它们?

感谢任何有经验的“泄密者”!

【问题讨论】:

我建议在弄乱内存使用之前调用GC,这样你就可以确定这不是一种计时问题。 @rekire:进行堆转储的行为会减少所有垃圾,因为垃圾无法访问。 “为什么不同设备的内存/GC行为不同?” -- 由于操作系统版本的差异,以及设备制造商所做的更改。 “在 S3 上,操作系统最终会回来并 GC 搁浅的活动 B 对象”——我们应该怎么知道? “(换句话说,我应该不用担心它,因为Android会在崩溃前清理它)?” -- 什么崩溃? @CommonsWare 谢谢你的提示。 @CommonsWare:您是否认为存在与调用完成()的活动相关的内存是我应该解决的问题?换句话说,如果我要不断地打开和关闭活动 B,从而增加滞留内存,是否有理由认为操作系统最终会清理它,或者应用程序更有可能最终崩溃? 【参考方案1】:

更新:为了查看活动 B 留下的“搁浅”内存是否真的有问题,我在活动 A 和 B 之间创建了一个循环:

--- A calls startActivityForResult() on B
--- B calls finish()
--- onActivityResult() in A calls B again, etc...

我让它飞行了大约 15k 次迭代,程序没有崩溃。因此,即使此手机和操作系统版本(Samsung S3,4.1.1)上的 JVM 在内存中保留了对 B 类的引用,即使在活动 B 调用了 finish() 之后,似乎也会进行某种稍后的清理,因为在测试期间,对 B 类的引用数量并没有单调增加。

事实上,我在测试期间生成了几个堆转储,并且从未看到超过 100 个 B 类实例——与我希望看到的 15k 相比,如果没有其他清理。 logcat 中的 GC 语句也显示了内存使用量的上升和下降,尽管有大量的活动 B 调用。

我的结论是,我不应该担心在完成()调用之后堆转储中存在 B 类引用,因为在此手机/Android 版本上使用的 JVM 似乎进行了某种延迟清理。

注意:这是我根据我运行的测试得出的结论,所以如果有人知道不同,请分享。谢谢!

【讨论】:

以上是关于调用 finish() 不会清除对 Activity 的内存引用的主要内容,如果未能解决你的问题,请参考以下文章

Android:清除活动堆栈

Android中finish掉其它的Activity

finish() 和 Activity 生命周期

切换活动而不会丢失自定义数据

即使在调用 [PayPalMobile clearAllUserData] 之后,Paypal ios SDK 也不会清除以前使用过的信用卡

汇聚,杉德,微信原生支付,支付宝成功/失败回调记录