1

我有一个应用程序在线程中获取位图,然后将位图放入全局内存缓存中。在 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 操作系统中,位图内存是单独存储的?我一直在做 hprof 转储并在 MAT 中查看它们,我的对象计数看起来符合预期。

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

无论哪种情况,有没有办法查看我的应用程序分配了哪些 Bitmap 实例?我认为 hprof 转储仅显示系统范围内的总计数。但是我看不到它们是从哪里分配的。

我在用着:

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

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

谢谢

4

2 回答 2

1

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

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

于 2012-04-22T16:44:47.620 回答
1

你是对的更高版本将位图数据保留在本机堆中。但即便如此,这些数据仍然计入位图预算。为避免此错误,您必须调用回收!所以扩展 LruCache,覆盖entryRemoved并在那里调用回收。此外,我会根据设备内存对缓存进行限制。例如,您可以使用ActivityManager.getMemoryClass()

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

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

这仍然不足以避免 OOM 错误,因为位图内存仅在终结器中被释放。请务必阅读有关位图处理的官方文档。如果一切都失败了,你仍然可以尝试打电话System.gc()entryRemoved看看是否有帮助。但正如 Ewoks 指出的那样,这不仅是一种不好的做法,而且还可能导致 UI 线程停止一段时间,所以只能作为最后的手段。

于 2012-04-22T16:47:01.830 回答