内存和活动 :: 内存溢出

5

安装 :

我有一个应用程序,其中包含4个具有线性路径的活动,导航非常简单: A -> B -> C -> D

所有活动都共享相同的背景图像,并且都具有一些常规按钮、文本视图或编辑文本。所有这些元素都在不同的视图xml文件中定义。

背景是渐变的,并且比较重。未压缩的位图大小约为3兆字节。

该应用程序还什么都没有实现,唯一的逻辑是在点击按钮时启动和关闭活动。

我尝试使用MAT查找内存泄漏,但找不到任何信息。我的应用程序中最大的保留大小为656(ko?),应用程序的总保留大小为1520(ko?),我找不到任何可能被复制的对象。 顺便说一下,这与dumpsys完全矛盾,dumpsys显示分配了27300(ko?)。

问题 :

  1. 当我向上导航时,我会看到内存使用量增加相当于背景大小的量。
  2. 当我向下导航时,使用后退按钮或完成命令关闭活动后,应用程序的内存使用量不会减少。
  3. 如果我从A转到D,然后返回B并旋转屏幕,则应用程序会因OutOfMemory异常而强制关闭。

问题 :

更新:我猜真正的问题是为什么我的应用程序有一个巨大的内存泄漏(每次5兆字节),在冻结时大小为27兆字节,但我无法在MAT中看到它?

  1. 为什么Android会多次解压相同的背景图,在每个活动中解压一次?似乎效率不高。
  2. 是否可以通过使用主题来解决此问题,还是会出现“为每个活动分配1个背景”的奇怪情况?
  3. 为什么关闭活动后,活动未被回收?
  4. 为什么MAT和dumpsys呈现不同的数字?

线索

正好在同一时间我有: dumpsys meminfo:

Applications Memory Usage (kB):
Uptime: 74006853 Realtime: 110962243

** MEMINFO in pid 22683 [com.kayenko.sosadresse] **
                    native   dalvik    other    total
            size:    20820     5767      N/A    26587
       allocated:    18751     2901      N/A    21652
            free:      312     2866      N/A     3178
           (Pss):     1357      201    16782    18340
  (shared dirty):     2264     1804     5456     9524
    (priv dirty):     1280      116    16032    17428

 Objects
           Views:        0        ViewRoots:        0
     AppContexts:        0       Activities:        0
          Assets:        2    AssetManagers:        2
   Local Binders:       18    Proxy Binders:       16
Death Recipients:        1
 OpenSSL Sockets:        0

 SQL
               heap:        0         MEMORY_USED:        0
 PAGECACHE_OVERFLOW:        0         MALLOC_SIZE:        0

这是支配树:

MAT 支配树

感谢任何有线索的人能告诉我该寻找什么。

3个回答

7

在Android中,内存是一个非常棘手的问题。

每个应用程序都有一个堆内存限制,具体取决于设备。这个堆内存是dalvik内存和本地内存的总和,你可以在dumpsys meminfo结果的总列中看到它。dalvik内存处理除位图外的所有内容,而位图则分配在本地内存中(对于Honeycomb之前的Android版本是这样的)。

话虽如此,我只能回答你一些问题:

  1. 据我所知,即使位图相同,Android也会为每个位图分配内存。因此,在你的情况下,每个活动都会为你的背景分配内存。

  2. 我不知道使用主题是否更好,你需要自己尝试。

  3. 一方面,只要设备有足够的内存处理下一个活动,活动就不会被回收。每个活动都被推到一个堆栈中,在按下返回按钮时从中恢复。如果Android需要更多的内存,它会从堆栈中删除一个活动并释放其内存(回到第一个问题,也许这就是不共享内存的原因)。另一方面,你可以设置活动的launchMode来改变这种行为(在这里查看:http://developer.android.com/guide/topics/manifest/activity-element.html#lmode)。

  4. 我认为MAT不显示本地内存数据。使用dumpsys meminfo的本地列来查看分配给位图的内存量。

我自己也曾经遇到过OutOfMemory问题,现在我对它的工作原理有了更清晰的认识,并且能够处理大文件而不会耗尽内存。我强烈推荐这两个帮助我很多的资源:

祝你好运!


谢谢Xavi,这非常有帮助。我已经查看了你给我的链接,但不幸的是没有什么帮助。我不知道launchmode,必须承认我对它应该做什么一无所知。我想我必须测试它,因为文档通常都是“缺乏”的 :) - Yahel
1
我会将你的回答标记为答案,因为你实际上给了我4个答案中的3个 :D - Yahel

4
经过数小时的调查和Xavi的帮助,以下是结果:
问题1:为什么Android会针对每个活动多次解压缩相同的背景图片?这似乎效率低下。
答案1:虽然在移动设备上内存较小,看起来有一种逻辑方式可以请求共享位图以跨活动使用,但这似乎在Android中不存在。每当位图在不同的活动中使用时,它都会被解压缩到本地内存中。
问题2:是否可以通过使用主题来解决此问题,还是会出现相同的“为每个活动分配一个位图”的问题?
答案2:经过实验,使用主题消耗的内存与在布局的xml中显式设置位图使用的内存没有任何不同。这对我来说很奇怪,因为样式化是将属性分组到同一位置。
问题3:为什么关闭活动后它们没有被回收?
答案3:我不确定,但我发现当调试时,这几乎只会给我带来OOM错误。从设备启动应用程序时,几乎从未发生过。调试过程中出现了故障吗?在测试无数事物之前,请先尝试一下。
问题4:为什么MAT和dumpsys显示的数字不同?
答案4:Xavi的答案是正确的。dumpsys meminfo显示分配的所有内存(本地+dalvik),而MAT仅显示Dalvik的内存。由于位图像素在本地内存中分配,MAT将看不到它。这仅适用于Android 3.0之前,在那之后更改了分配方案,并使位图的像素数据适合Dalvik。
问题5:我如何解决我的问题?
答案5:首先,如果不是在调试时,可能并没有问题。其次,为了保险起见,我使用具有径向渐变的形状替换了渐变png,并使用
getWindow().setFormat(PixelFormat.RGBA_8888);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DITHER);

在我的活动的onCreate方法中,我尝试避免色带现象。虽然有些设备仍会出现色带现象,但我宁愿选择色带现象而不是应用崩溃。


0

当你销毁活动时,需要显式回收用作背景的位图。代码将如下所示:

@Override
protected void onDestroy () {
    Drawable drawable = getView().getBackground();
    if (drawable instanceof BitmapDrawable) {
        ((BitmapDrawable)drawable).getBitmap().recycle();
    }
    drawable.setCallback(null);
    getView().setBackgroundDrawable(null);
    super.onDestroy();
}

也许您需要递归地释放嵌套视图的资源,但这取决于您的布局结构。这是一般情况。

感谢您的回复,但很遗憾它并没有帮助到我。根据您提供的代码,我的任何小部件都没有bitmapdrawables,我还尝试了这个页面上的代码:http://www.alonsoruibal.com/bitmap-size-exceeds-vm-budget/,然后将两者混合使用以尽可能地采取措施,但仍然无济于事。 - Yahel
我不同意。Dalvik会在没有引用时自动回收位图。没有必要手动执行此操作,recycle()方法存在是为了让您加快此过程并提前回收内存。 - Kevin Read

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接