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

问题描述:

我有一个超过600x800像素JPEG的图库的OutOfMemory异常。Android:OutofMemoryError:位图大小超过虚拟机预算,没有任何理由我可以看到


的环境我一直在使用画廊周围600X800像素的JPG图片

由于我的内容可能比图片稍微复杂一些,因此我已将每个视图设置为一个将ImageView换成JPG的RelativeLayout。为了“加快”用户体验,我有一个4槽的简单缓存,它可以在一个循环中预取约1张图像和1张图像,并将它们保存在4个槽位的HashMap中。

我使用的256 RAM和128堆大小AVD,具有600×800的屏幕的平台。 它也发生在Entourage Edge目标上,除了使用设备更难调试。


我已经越来越异常的问题:

OutofMemoryError: bitmap size exceeds VM budget 

和打水第五影像画面,当它发生。我试图改变我的图像缓存的大小,它仍然是一样的。


奇怪的:不应该有一个内存问题

为了确保堆限制是从很远我需要什么,我已经定义的虚拟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。它怎么还不够?

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


  • 难道是因为我遇到像堆碎片一些问题呢?
  • 我该如何解决/解决问题?
  • 堆是否共享到所有线程?
  • 难道是我以错误的方式解读DDMS读数,真的没有900K块要分配吗?如果是这样,任何人都可以告诉我在哪里可以看到?

非常感谢

Meymann

+0

如果您发布实际获取/解码/缓存/到期位图的代码,您可能会在这里找到一些答案。问题几乎可以肯定你在那里做什么,而不需要深入堆内部分配。 – 2010-06-14 16:08:14

+0

调试代码时,我的主要嫌疑人总是我的代码。为了调试它,我从环境中获得提示。不幸的是,在这种情况下:A.创建一个简单的代码,每次读取3张600x800图像到缓存中会偶尔产生类似的结果(检查,以便更快地发生,可以添加虚拟未引用的数组),B.我使用这些工具来探究这个问题,但是我从工具中得到的提示并不一致。 C.这个问题的要点是获得什么是好的做法,我的DDMS读数结论有什么问题,以及是否有解决方法。 – Meymann 2010-06-14 20:07:24

+1

**以另一种形式重现问题的一种非常简单的方法** 1.在您的Activity类中,添加静态{byte dummy [] = new byte [4096]; }强制堆扩展(并消除疑惑)。 2.创建一个ViewFlipper。 3.添加大约10个ImageView,其中每个引用可绘制的600x800位图。 4.当它崩溃时,看看DDMS。 – Meymann 2010-06-21 11:50:03

我认为你的情况没有什么特别之处。没有足够的内存。内存中不能有多个600x800位图,它们消耗的内存太多。您应该将它们保存到SD并按需加载到内存中。我认为这正是你所做的。

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

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

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

这里有一个关于内存分配一个不错的post,它可以帮助你更深入

+0

当然,从SD延迟加载是我一直在做的。这就是画廊正在做的事情。不过,我使用4插槽高速缓存来加速它。尽管如此,我还需要了解三点:1.如何强制Gallery尽快发送未使用的图像? 2.这是不够的? 800x600x4imagesx3bytesPerPixel = 5M,小于16M。我想知道它为什么如此零星。有时它是有效的,并且它总是在不同的地方飞行。谢谢 – Meymann 2010-07-07 07:02:53

+3

当您使用4插槽缓存时,您可以在从此缓存中删除旧映像时调用recycle()。 – Fedor 2010-07-07 10:15:44

+1

在适配器中调用getView时,convertView会传递给您。其实这是一个正在被回收的观点。此视图上显示的位图实例不再需要。所以我想你可以在该ImageView上显示位图并回收它。这与ASAP方法接近。 – Fedor 2010-07-07 12:05:07

不知道,如果它是一个选择,但你有没有尝试过取样图像Strange out of memory issue while loading an image to a Bitmap object

+1

谢谢,但我担心它不会这样做......它杀死了图像的质量,因为它可以让解码器更“sl”“。因此,图像的文本和小部分会变得非常模糊......就像JPEG知道如何涂抹......它实际上与采用JPEG EXIF缩略图并将其调整为全尺寸时发生的情况类似。谢谢 – Meymann 2010-07-06 08:44:44

我也面临类似的问题,几个星期回来,我通过按比例缩小图像高达最佳点解决它。我在我的博客here中编写了完整的方法,并上传了带有OOM代码的完整示例项目,与OOM验证代码here

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

答案应该分为2部分: 姜饼姜饼:你只是使用小图片,使用二次采样,也许一个屏幕大小的照片,并希望好。尝试确保在获取位图之前不分配无法释放的小物件。前姜,bmps的内存必须是contonuous,并且它不计入VM内存。总是看看logcat。查看来自Google IO 2011的关于记忆的讲座。 Post Ginger更简单。自从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倍。