BitmapFactory OOM驱使我坚果

我一直在进行大量的搜索,并且我知道很多其他人正在经历与BitmapFactory相同的OOM内存问题。 我的应用程序只使用Runtime.getRuntime ().totalMemory()显示可用的总内存为4MB。 如果限制是16MB,那么为什么总内存不会增加以为位图腾出空间? 相反,它会抛出一个错误。

我也不明白,如果根据Runtime.getRuntime().freeMemory()我有1.6MB的可用内存Runtime.getRuntime().freeMemory()为什么我得到一个错误说“虚拟机不会让我们分配614400字节”? 在我看来,我有足够的可用内存。

我的应用程序是完整的,除了这个问题,当我重新启动手机时,这个问题就消失了,这样我的应用程序是唯一运行的。 我正在使用HTC Hero进行设备测试(Android 1.5)。

在这一点上,我想唯一的方法就是避免使用BitmapFactory

任何人有任何想法或解释为什么虚拟机将不会分配614KB时有1.6MB的可用内存?


[注意(正如CommonsWare在下面指出的),这个答案中的整个方法只适用于2.3.x(姜饼)。 从Honeycomb开始位图数据在VM堆中分配。]

位图数据未在VM堆中分配。 在VM堆(它很小)中有一个对它的引用,但实际的数据是由底层的Skia图形库在Native堆中分配的。

不幸的是,虽然BitmapFactory.decode ...()的定义表示如果图像数据无法解码,它将返回null,但Skia实现(或者说Java代码和Skia之间的JNI粘合)会记录您看到(“虚拟机不会让我们分配xxxx字节”),然后通过误导性消息“位图大小超过虚拟机预算”抛出OutOfMemory异常。

问题不在VM堆中,而是在本机堆中。 Natïve堆是在正在运行的应用程序之间共享的,因此可用空间的大小取决于正在运行的其他应用程序以及它们的位图使用情况。 但是,鉴于BitmapFactory不会返回,您需要一种方法来确定在调用之前调用是否成功。

有例程监视本地堆的大小(请参阅Debug类的getNative方法)。 但是,我发现getNativeHeapFreeSize()和getNativeHeapSize()不可靠。 所以在我的一个动态创建大量位图的应用程序中,我执行以下操作。

本地堆大小因平台而异。 因此,在启动时,我们检查允许的最大VM堆大小,以确定允许的最大本机堆大小。 [神奇数字是通过2.1和2.2测试确定的,并且可能与其他API级别不同。]

long mMaxVmHeap     = Runtime.getRuntime().maxMemory()/1024;
long mMaxNativeHeap = 16*1024;
if (mMaxVmHeap == 16*1024)
     mMaxNativeHeap = 16*1024;
else if (mMaxVmHeap == 24*1024)
     mMaxNativeHeap = 24*1024;
else
    Log.w(TAG, "Unrecognized VM heap size = " + mMaxVmHeap);

然后每次我们需要调用BitmapFactory时,我们都会先检查表单,然后再调用BitmapFactory。

long sizeReqd        = bitmapWidth * bitmapHeight * targetBpp  / 8;
long allocNativeHeap = Debug.getNativeHeapAllocatedSize();
if ((sizeReqd + allocNativeHeap + heapPad) >= mMaxNativeHeap)
{
    // Do not call BitmapFactory…
}

请注意,heapPad是一个神奇数字,它允许a)本机堆大小的报告为“软”,b)我们希望在本机堆中为其他应用程序留出一些空间。 我们目前正在运行3 * 1024 * 1024(即3M字节)的焊盘。


1.6 MB的内存看起来很多,但可能是内存碎片太多以至于无法一次性分配这么大的内存(这听起来很奇怪)。

使用图像资源时,OOM的一个常见原因是当您使用真正高分辨率解压缩JPG,PNG和GIF图像时。 你需要记住,所有这些格式都压缩得非常好,占用的空间非常小,但是一旦你将图像加载到手机上,他们要使用的内存就像width * height * 4 bytes 。 而且,当解压缩开始时,需要为解码步骤加载一些其他辅助数据结构。


似乎Torid的答案给出的问题已在最新版本的Android中得到解决。

但是,如果您正在使用图像缓存(一种专门的或甚至只是一个常规的HashMap),则通过创建内存泄漏来获取此错误相当容易。

根据我的经验,如果您无意中持有Bitmap引用并创建内存泄漏,则OP的错误(引用BitmapFactory和本地方法)会导致应用程序崩溃(直到ICS-14和+?)。

为了避免这种情况,让你“放手”你的Bitmaps。 这意味着在缓存的最后一层使用SoftReferences,以便Bitmaps可以从中收集垃圾。 这应该可以工作,但是如果你仍然崩溃,你可以尝试使用bitmap.recycle()明确地标记某些bitmap.recycle()进行收集,只要记住,如果bitmap.isRecycled()不会返回位图在你的应用中使用。

顺便说一句, LinkedHashMaps是一个很好的工具,可以轻松实现相当不错的缓存结构,尤其是如果您结合本示例中的硬引用和软引用(起始行308) ......但使用硬引用也是如何让自己进入内存如果你搞砸了,会泄漏情况。

链接地址: http://www.djcxy.com/p/16623.html

上一篇: BitmapFactory OOM driving me nuts

下一篇: What does the filter parameter to createScaledBitmap do?