【问题标题】:Calling finish() does not clear memory references to Activity调用 finish() 不会清除对 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 内存的引用。

我有三个问题。

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

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

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

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

【问题讨论】:

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

标签: android memory-leaks back-stack android-memory mat


【解决方案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 似乎进行了某种延迟清理。

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

【讨论】:

    猜你喜欢
    • 2015-05-16
    • 2010-12-30
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 2012-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多