我有一个应用程序在线程中获取位图,然后将位图放入全局内存缓存中。在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(),也许它们没有得到正确的清理?
由于
答案 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停止一段时间,所以这只是最后的手段。