Android:OutofMemoryError:位图大小超过虚拟机预算,没有任何理由,我可以看到

我有一个超过600×800像素JPEG的画廊OutOfMemoryexception。


环境

我一直在用600×800像素的JPG图片来使用Gallery。

由于我的内容可能比图片稍微复杂一些,因此我将每个视图都设置为一个将ImageView换成JPG的RelativeLayout。

为了“加快”用户体验,我有一个简单的4槽高速caching(在循环中),左边1个图像和右边1个图像预取到显示的图像,并保留在4槽HashMap中。

该平台

我正在使用256 RAM和128堆大小的AVD,以及600×800的屏幕。 它也发生在Entourage Edge目标上,除了使用设备更难debugging。


问题

我已经得到一个例外:

OutofMemoryError: bitmap size exceeds VM budget 

当获取第五张图片时会发生这种情况。 我试图改变我的图像caching的大小,它仍然是一样的。


奇怪的事情:不应该有一个记忆问题

为了确保堆的限制与我所需要的距离相去甚远,我在开始时定义了一个虚拟的8MB数组,并将其保留为未引用状态,以便立即派发。 它是活动线程的成员,定义如下

 static { @SuppressWarnings("unused") byte dummy[] = new byte[ 8*1024*1024 ]; } 

结果是堆大小接近11MB,而且全部是免费的。 注意我开始崩溃后添加了这个技巧。 这使得OutOfMemory不那么频繁。

现在,我正在使用DDMS。 就在崩溃之前(在崩溃之后变化不大),DDMS显示:

 ID Heap Size Allocated Free %Used #Objects 1 11.195 MB 2.428 MB 8.767 MB 21.69% 47,156 

并在详细表中显示:

 Type Count Total Size Smallest Largest Median Average free 1,536 8.739MB 16B 7.750MB 24B 5.825KB 

最大的块是7.7MB。 然而,LogCat说:

 ERROR/dalvikvm-heap(1923): 925200-byte external allocation too large for this process. 

如果你介意中位数和平均数的关系,假设大多数可用的数据块非常小是合理的。 但是, 有一个足够大的位图块,它是7.7M。 怎么还不够呢?

注意:我logging了一个堆跟踪。 在查看分配的数据量时,感觉不到超过2M的分配。 它与DDMS的空闲内存报告相匹配。


  • 难道我会遇到像堆碎片这样的问题吗?
  • 我如何解决/解决问题?
  • 是堆共享到所有线程?
  • 难道是我以错误的方式解读DDMS的读数,而且真的没有900K块的分配? 如果是这样,有谁可以告诉我在哪里可以看到?

非常感谢

Meymann

我觉得你的情况没有什么特别的。 没有足够的记忆。 内存中不能有多个600×800位图,它们消耗的内存太多。 您应该将它们保存到SD并按需加载到内存中。 我想这正是你所做的。

有一点你应该注意:DDMS显示java堆内存消耗。 但是也有本地内存不在DDMS中显示。 据我所知,位图是在本机内存中创build的。 所以DDMS只是跟踪这些内存问题的一个糟糕的工具。 你只需要确保你释放了你的内存,在你不需要它们之后,垃圾收集器会收集到这些图像。

垃圾收集器按照自己的时间表工作。 这就是为什么你应该在不需要的位图上调用Bitmap.recycle()方法。 这个方法完全释放你用尽的本地内存。 这样你就不依赖于GC,并且可以尽快释放最大的内存。

首先你应该确保你不会泄漏位图。

这里有一个很好的内存分配的post ,它可以帮助你深入挖掘

不知道这是否是一个选项,但你有没有尝试超采样图像奇怪的内存不足的问题,同时加载图像到Bitmap对象 ?

我几周前还面临类似的问题,我通过缩小图像到最佳点来解决这个问题。 我在这里写了完整的方法,并在这里上传了完整的示例项目,其中包含OOM容易代码和OOM Proof代码。

自从我问这个问题以来,已经有很多时间了。

答案应该分为两部分:姜饼:你只是使用小图片,使用二次采样,也许一个屏幕大小的照片,并希望好。 尝试确保在获取位图之前不要分配无法释放的小项目。 前姜,bmps的内存必须是contonuous,并没有计入VM内存。 总是看看logcat。 查看来自Google IO 2011的关于记忆的讲座。发布生姜比较容易。 自从Honeycomb以来,位图甚至被计入你的java区域。 没有jni区域。 总是使用您不需要的位图的回收。 不要等待GC。

这个问题在2010年被问到,当时Froyo是新鲜的。 自那以后发生了很多事情。 在3.0之前,位图被分配在JNI中。 内存没有显示在Dalvik统计。 它不一定是单一的。 在2.3之前,在logcat中JNI的内存统计信息不可用(位图解码调用JNI)。 4.4撤离更多的空间。 5.0艺术大爆炸。 早在2010年,Nexus One就是高端,不到300MB。 一个应用程序的预算是16MB左右。 现在几天,这个记忆大概是8倍。