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

时间:2011-07-26 19:26:05

标签: java android memory-leaks memory-management

这个问题涉及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在onDestroy of B中将其背景设置为null。
  • 我确信B会被摧毁。

结果:

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

  • 我没有查看堆限制,我正在查看已分配的堆。
  • 我在B的onDestroy方法结束时调用System.gc()。

其他信息:

- 我使用MAT来分析内存分配并试图找到这个漏洞。奇怪的是,活动B似乎有5个实例。碰巧的是,我重复了startActivity / finish过程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”属性并且内存泄漏又回来了。这有多奇怪?

5 个答案:

答案 0 :(得分:11)

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

adb shell dumpsys meminfo'您的应用包名称'

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

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

一旦我完成了这个,那么当我在它们之间切换时,adb shell命令没有显示我的两个活动的更多实例

答案 1 :(得分:3)

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

可能你正在泄露上下文(在这种情况下的活动)。因此,请确保在活动中调用onDestroy方法时,正在清理对上下文的所有引用。 More details here

另外,请查看完成活动后未注册的可能内容观察者。

答案 2 :(得分:2)

我认为,这是一个典型的java问题。杀死一个Activity并不意味着它的关联对象应该从heap中删除,即使它们在丢失的土地上(它们的引用为空)。因为它在调用Garbage collector时完全依赖虚拟机(无论你说System.GC())。因此当条件几乎没有记忆时它会调用并清理它(不能再确定,可能会在它们被引用后立即变为null),所以我认为你不应该担心它。

<强>编辑: 致电setContentView(getApplicationContext);

以及this关键字在哪里传递上下文,请将其更改为this.getApplicationContext()

答案 3 :(得分:1)

考虑使用

android:launchMode="singleTask"
在AndroidManifest.xml中

See this

答案 4 :(得分:0)

  

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

当系统认为需要时,将回收0.08MB的堆(如果未使用)。一旦调用System.gc(),垃圾收集就不会发生,它更像是在可能的情况下尽快发生GC的请求。在分配对象和从内存中删除对象之间通常会有 LONG 时间。 Java虚拟机规范和Java语言规范指定了经历以下阶段的对象的生命周期:

  1. 创建
  2. 使用中(强烈可达)
  3. 隐形
  4. 无法到达
  5. 累计
  6. 结算
  7. 释放
  8. 请注意,当一个对象无法访问时,它只会成为垃圾收集的候选者,因为它可能发生。 即使像Dalvik这样占地面积小的JVM,我也不认为物体会在短时间内经历整个寿命。 GC是一项昂贵的操作,因此仅在需要时才进行。

    如果您有快速(由于某种原因)需要从内存中删除的对象,请尝试WeakReference。或者也许你可以提供更多关于你想要做什么的背景信息,而且有人能够以这种方式帮助你。

    有关GC如何在Java中发生的更多信息,     this is a pretty good article