android - BitmapFactory OOM 让我抓狂

标签 android out-of-memory android-bitmap

我一直在做很多搜索,我认识很多其他人 BitmapFactory 遇到同样的 OOM 内存问题。我的 应用程序仅使用 Runtime.getRuntime 显示可用总内存为 4MB ().totalMemory()。如果限制是 16MB,那么为什么总不 内存增长为位图腾出空间?相反,它会引发错误。

我也不明白如果我有 1.6MB 的可用内存 到 Runtime.getRuntime().freeMemory() 为什么我会收到一条错误消息“VM 不会让我们分配 614400 字节”?在我看来,我有很多 可用内存。

我的应用程序是完整的,除了这个问题,当我消失时 重新启动手机,以便我的应用程序是唯一运行的东西。我在用着 用于设备测试的 HTC Hero (Android 1.5)。

在这一点上,我认为解决这个问题的唯一方法就是以某种方式 避免使用 BitmapFactory

任何人对此有任何想法或解释为什么 VM 不会 有 1.6MB 可用内存时分配 614KB?

最佳答案

[请注意(正如 CommonsWare 在下面指出的那样)此答案中的整个方法仅适用于并包括 2.3.x( Gingerbread )。从 Honeycomb 开始,位图数据在 VM 堆中分配。]

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

不幸的是,虽然 BitmapFactory.decode...() 的定义说如果无法解码图像数据,它会返回 null,但 Skia 实现(或者更确切地说是 Java 代码和 Skia 之间的 JNI 粘合)记录了该消息您会看到(“VM 不会让我们分配 xxxx 字节”),然后引发 OutOfMemory 异常,并带有误导性消息“位图大小超出 VM 预算”。

问题不在于 VM 堆,而在于 native 堆。 Natïve 堆在正在运行的应用程序之间共享,因此可用空间量取决于正在运行的其他应用程序及其位图使用情况。但是,鉴于 BitmapFactory 不会返回,您需要在调用之前确定调用是否会成功。

有一些例程可以监控 Native 堆的大小(参见 Debug 类的 getNative 方法)。但是,我发现 getNativeHeapFreeSize() 和 getNativeHeapSize() 并不可靠。因此,在我的一个动态创建大量位图的应用程序中,我执行以下操作。

native 堆大小因平台而异。因此,在启动时,我们检查允许的最大 VM 堆大小以确定允许的最大 Native 堆大小。 [magic numbers 是通过在 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 时,我们都会在调用之前检查表单。

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

请注意,heapPad 是一个神奇的数字,它允许以下事实:a) native 堆大小的报告是“软”的,并且 b)我们希望在 native 堆中为其他应用程序留出一些空间。我们目前正在使用 3*1024*1024(即 3Mbytes)的 pad 运行。

关于android - BitmapFactory OOM 让我抓狂,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1955410/

相关文章:

java - 错误: deleting all rows instead of 1 row Android

java - 未初始化的对象与新对象

java - 分割位图文件会产生 y + height 必须 <= bitmap.height() 错误

android - 使用 ImageView ScaleType 设置 TransitionDrawable

java - 如何设置 MEDIASTORE 以从 android 上的特定文件夹获取图像

android - 无法解析 SerializedName

android - 在 TabLayout 前面显示上下文操作栏

android - 广播接收器 - 调用者验证

go - 解析 Freebase RDF 时 golang 内存不足

java - Rapidminer - 处理大型数据集时内存不足