搞清楚位图的分配位置?

时间:2012-04-22 15:51:06

标签: android

我有一个应用程序在线程中获取位图,然后将位图放入全局内存缓存中。在2.2模拟器上,加载足够的位图后,我得到了可重现的内存不足错误(它们在列表视图中):

FATAL EXCEPTION: pool-1-thread-1
  java.lang.OutOfMemoryError: bitmap size exceeds VM budget
  at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
  ...

堆看起来稳定(3.3mb),我想知道我是否看不到内存量的增长,因为我认为在2.2 os中,位图内存是分开存储的?我继续做hprof转储并在MAT中查看它们,我的对象计数看起来像预期的那样。

在ICS手机上运行应用程序不会出现同样的问题(但可能只有更多的内存等)。我认为在后来的操作系统中,他们将位图内存保留为堆的一部分?

在任何一种情况下,有没有办法查看我的应用分配了哪些Bitmap实例?我认为hprof转储只显示系统范围内的总数。但我看不出他们从哪里分配。

我正在使用:

android.support.v4.util.LruCache<String, Bitmap> 

作为内存缓存 - 几乎看起来在条目被驱逐后,位图数据没有被清理(我将缓存设置为5mb上限)。我记得Bitmap上的方法就像recycle(),也许它们没有得到正确的清理?

由于

2 个答案:

答案 0 :(得分:1)

  

堆看起来稳定(3.3mb),我想知道我是否看不到数量   内存增长因为我认为在2.2 os中,位图内存是   单独存放?我一直在做hprof转储并在MAT中查看它们,   并且我的对象计数看起来像预期的那样。

正确,对于2.x,您无法看到位图使用MAT所占用的内存(您可以从ICS设备进行转储并突然看到一个巨大的跳转)。但是,可用的内存量是相同的(虽然取决于设备,但一般来说,它介于16 MB和32 MB之间)。解决方案是尽可能少地保留内存中的位图。如果它们只是缩略图大小,你可能会将它们保留在内存中,但通常你只能加载那么多。

答案 1 :(得分:1)

你是对的,版本将位图数据保存在本机堆中。但即便如此,这些数据仍然与位图预算相关。要避免此错误,您必须致电回收!因此,扩展LruCache,覆盖entryRemoved并在那里调用recycle。 另外,我会根据设备内存对缓存设置限制。例如,您可以使用ActivityManager.getMemoryClass()

    ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    int memoryClass = activityManager.getMemoryClass();

memoryClass包含以兆字节为单位的dalvik堆。例如,您可以使用该值的1/5作为缓存的限制。

这仍然不足以避免OOM错误,因为位图内存仅在终结器中释放。请务必阅读有关位图处理的official doc。如果全部失败,您仍然可以尝试在System.gc()中致电entryRemoved,看看是否有帮助。但正如Ewoks指出的那样,这不仅是不好的做法,而且还可能导致UI-Thread停止一段时间,所以这只是最后的手段。