BitmapFactory OOM驱使我坚果

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

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

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

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

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

Solutions Collecting From Web of "BitmapFactory OOM驱使我坚果"

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

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

不幸的是,虽然BitmapFactory.decode …()的定义说,如果图像数据不能被解码,它将返回null,Skia实现(或者说Java代码和Skia之间的JNI粘合)会logging你看到(“虚拟机不会让我们分配xxxx字节”),然后引发OutOfMemoryexception与误导性消息“位图大小超过虚拟机预算”。

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

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

本地堆大小因平台而异。 因此,在启动时,我们检查允许的最大VM堆大小,以确定允许的最大本机堆大小。 [神奇数字是由2.1和2.2testing确定的,在其他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中得到解决。

但是,如果您正在使用图像caching(一个专门的或甚至只是一个常规的HashMap),通过创build内存泄漏很容易得到这个错误。

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

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

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

虽然通常抓住一个错误是没有意义的,因为通常它们只是由vm引发,但在这种情况下,jni胶合代码抛出了错误,因此处理无法加载图像的情况非常简单:只是捕获OutOfMemoryError。

虽然这是一个相当高的水平的答案,但对我来说,问题竟然是在我所有的观点上使用硬件加速。 我的大部分视图都具有自定义位图操作,我认为这是大本地堆大小的来源,但实际上禁用硬件加速时,本机堆的使用量减less了4倍。

看起来好像硬件加速将在你的视图上进行各种caching,创build自己的位图,并且由于所有的位图共享本地堆,分配的大小可以大大增加。