为什么重新启动活动时堆内存会增加?

这个问题涉及Android中的内存。

我的方法:

我有两个活动,A和B。从A开始,我像这样启动B:

Intent i = new Intent(A.this, B.class);
startActivity(i);

在按钮上单击B,我这样做:

B.this.finish();
  • 在 B 中,我重写 onDestroy 方法并将所有引用设置为 null。
  • 我没有在A的onResume方法中分配新内存。
  • 我不是在泄露上下文。
  • 我没有使用多个线程。
  • 我没有使用服务。
  • B 中的所有变量都是私有类变量,在 B 的 onDestroy 中,所有这些变量都设置为 null。
  • 此外,B 中的 ImageViews 在 B 的 onDestroy 中将其背景设置为 null。
  • 我确信B被摧毁了。

结果:

当我在活动 A 中时,堆内存为 7.44 MB。然后,当我启动 B 并在 B 上调用 finish(从而返回到 A)时,堆增加了 0.16 MB。再次重复此过程,堆每次都会增加 0.08 MB。

  • 我不是在查看堆限制,而是在查看分配的堆。
  • 我在B的onDestroy方法的末尾调用System.gc()。

附加信息:

- 我使用MAT来分析内存分配并尝试找到此泄漏。奇怪的是,活动 B 似乎有 5 个实例。碰巧的是,我重复了开始活动/完成过程5次。底部条目是活动,其他条目是活动中的侦听器:

enter image description here

这是支配者树的截图。我找不到任何异常或可疑的东西。

Dominator Tree

- 我看过两个关于内存使用情况(和泄漏)的谷歌IO视频。

问题:

无论我做什么,这 0.08 MB 的堆是否有可能始终被分配(并且 GC 无法收集)?如果没有,任何想法可能导致这种情况?

更新:

  1. 我试图在不设置B内容视图的情况下启动活动B。这意味着 B 是一个完全空的活动。结果是,当我多次重新启动活动时,堆内存没有增加。但请注意,这不是解决方案。我必须能够设置内容视图。

  2. scorpiodawg:我尝试在模拟器上运行我的应用程序,但堆仍在增长。不过很好。

  3. ntc:在可能的情况下,我将“this”的所有出现都更改为“getApplicationContext()”。我无法调用 setContentView(getApplicationContext());因为 setContentView 需要对布局文件的引用,而不是上下文。相反,我所做的是创建一个空的布局文件并调用setContentView(emptylayout);在活动 B 的 onDestroy 方法中。这无济于事。

  4. 我试图删除所有代码,以便只调用setContentView(mylayout)。问题仍然存在。然后,我删除了布局 XML 文件中的所有 gui 元素。问题仍然存在。剩下的唯一东西是容器视图,几个嵌套的线性,相对和滚动布局。我试图删除滚动条中的设置“android:scrollbarDefaultDelayBeforeFade”属性。结果很棒,内存泄漏消失了。然后我放回了我之前删除的所有代码,但没有设置“android:scrollbarDefaultDelayBeforeFade”属性,内存泄漏又回来了。这有多奇怪?


答案 1

如果您有 5 个活动 B 实例,则说明您没有正确管理活动堆栈。我发现检查它的最佳方法是使用CLI命令:

adb shell dumpsys meminfo 'your apps package name'

当我在两个活动项目之间切换时,我在两个活动项目中遇到了类似的问题。每次切换时,我都会在堆栈上获得一个新实例,如上述命令所示。然后,我将已启动活动的标志设置为使用如下代码FLAG_ACTIVITY_REORDER_TO_FRONT:

Intent i = new Intent("com.you.yourActivityB");
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(i);

完成此操作后,当我在两个活动之间切换时,adb shell 命令不会显示我的两个活动的更多实例。


答案 2

似乎您在该活动中有内存泄漏。

您可能正在泄漏上下文(本例中的活动)。因此,请确保在活动中调用 onDestroy 方法时清理对上下文的所有引用。更多详细信息请点击此处

此外,请查看完成活动时可能未取消注册的内容观察者。


推荐