Android:OutofMemoryError:位图大小超出 VM 预算,没有任何原因我看不到
我的画廊超过 600x800 像素 JPEG 时遇到内存不足异常。
环境
我一直在使用 Gallery,其中包含 600x800 像素左右的 JPG 图像。
由于我的内容可能比图像复杂一些,因此我将每个视图设置为使用 JPG 包装 ImageView 的relativelayout。
为了“加速”用户体验,我有一个 4 个插槽的简单缓存,它预取(在循环程序中)显示图像左侧的 1 个图像和右侧的 1 个图像,并将它们保存在 4 个插槽的 HashMap 中。
平台
我使用的是 256 RAM 和 128 堆大小的 AVD,屏幕尺寸为 600x800。 它也会发生在 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 块可供分配?如果是这样,有人可以告诉我在哪里可以看到吗?
非常感
谢梅曼
I am having an OutOfMemory exception with a gallery over 600x800 pixels JPEG's.
The environment
I've been using Gallery with JPG images around 600x800 pixels.
Since my content may be a bit more complex than just images, I have set each view to be a RelativeLayout that wraps ImageView with the JPG.
In order to "speed up" the user experience I have a simple cache of 4 slots that prefetches (in a looper) about 1 image left and 1 image right to the displayed image and keeps them in a 4 slot HashMap.
The platform
I am using AVD of 256 RAM and 128 Heap Size, with a 600x800 screen.
It also happens on an Entourage Edge target, except that with the device it's harder to debug.
The problem
I have been getting an exception:
OutofMemoryError: bitmap size exceeds VM budget
And it happens when fetching the fifth image. I have tried to change the size of my image cache, and it is still the same.
The strange thing: There should not be a memory problem
In order to make sure the heap limit is very far away from what I need, I have defined a dummy 8MB array in the beginning, and left it unreferenced so it's immediately dispatched. It is a member of the activity thread and is defined as following
static { @SuppressWarnings("unused")
byte dummy[] = new byte[ 8*1024*1024 ]; }
The result is that the heap size is nearly 11MB and it's all free.
Note I have added that trick after it began to crash. It makes OutOfMemory less frequent.
Now, I am using DDMS. Just before the crash (does not change much after the crash), DDMS shows:
ID Heap Size Allocated Free %Used #Objects
1 11.195 MB 2.428 MB 8.767 MB 21.69% 47,156
And in the detail table it shows:
Type Count Total Size Smallest Largest Median Average
free 1,536 8.739MB 16B 7.750MB 24B 5.825KB
The largest block is 7.7MB. And yet the LogCat says:
ERROR/dalvikvm-heap(1923): 925200-byte external allocation too large for this process.
If you mind the relation of the median and the average, it is plausible to assume that most of the available blocks are very small. However, there is a block large enough for the bitmap, it's 7.7M. How come it is still not enough?
Note: I recorded a heap trace. When looking at the amount of data allocated, it does not feel like more than 2M is allocated. It does match the free memory report by DDMS.
- Could it be that I experience some problem like heap-fragmentation?
- How do I solve/workaround the problem?
- Is the heap shared to all threads?
- Could it be that I interpret the DDMS readout in a wrong way, and there is really no 900K block to allocate? If so, can anybody please tell me where I can see that?
Thanks a lot
Meymann
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我认为你的情况没有什么特别的。只是内存不够。内存中不能有多个 600x800 位图,它们会消耗太多内存。您应该将它们保存到 SD 并按需加载到内存中。我认为这正是你所做的。
您应该注意的一件事:DDMS 显示 java 堆内存消耗。但还有一些本机内存未在 DDMS 中显示。据我所知,位图是在本机内存中创建的。所以 DDMS 只是跟踪这些内存问题的一个糟糕工具。您只需要确保释放内存,垃圾收集器会在您不再需要它们后收集图像。
垃圾收集器按照自己的时间表工作。这就是为什么您应该对不再需要的位图调用 Bitmap.recycle() 方法。此方法准确地释放您用完的本机内存。这样你就可以不依赖GC,并且可以尽快释放最大的一块内存。
首先,您应该确保不会泄漏位图。
这是一篇不错的帖子关于内存分配,它可以帮助您更深入地挖掘
I think there's nothing special in your case. There's just not enough memory. You can't have several 600x800 bitmaps in memory, they consume too much memory. You should save them to SD and load to memory on demand. I think that's exactly what you do.
One thing you should be aware of: DDMS displays java heap memory consumption. But there's also native memory that is not displayed in DDMS. And bitmaps as far as I understand are created in native memory. So DDMS is just a bad tool to track these memory issues. You just need to be sure that you free your memory, that images are collected by Garbage Collector after you don't need them any more.
Garbage Collector works on it's own schedule. That's why you should call Bitmap.recycle() method on bitmaps that you don't need any more. This method frees exactly the native memory that you run out of. This way you don't depend on GC and you can free largest piece of memory as soon as possible.
First of all you should ensure that you don't leak bitmaps.
Here's a nice post on memory allocations, it can help you to dig deeper
不确定这是否适合您,但您是否尝试过对图像进行超级采样奇怪将图像加载到位图对象时出现内存不足问题?
Not sure if it's an option for you, but have you tried supersampling images Strange out of memory issue while loading an image to a Bitmap object?
几周前我也遇到了类似的问题,我通过将图像缩小到最佳点来解决它。我在我的博客 此处,并在此处上传了包含 OOM 倾向代码与 OOM 证明代码的完整示例项目。
I Also faced similar issue couple of weeks back and i solved it by scaling down images upto optimal point. I have written complete approach in my blog here and uploaded complete sample project with OOM prone code vs OOM Proof code here.
自从我问这个以来已经有很多时间了。
答案应该分为两部分:
Pre-Gingerbread:您只需使用小图片,使用子采样,也许一张屏幕大小的照片,并希望有好的结果。尝试确保在获取位图之前没有分配无法释放的小项目。在 Ginger 之前,bmp 的内存必须是连续的,并且不计入 VM 内存。始终查看 logcat。请参阅 Google IO 2011 关于内存的讲座。
发姜后就更容易了。从 Honeycomb 开始,位图甚至算在 java 区域中。没有jni区域。
始终对不需要的位图使用回收。不要等待 GC。
There has been lots of time since I asked that.
The answer should be divided to 2 parts:
Pre-Gingerbread: you just use small pictures, use subsampling, maybe one screen size photo, and hope for good. Try to make sure you don't allocate tiny items you can't free before getting a bitmap. Pre-Ginger, the memory for bmps had to be contonuous, and it was not counted in the VM memory. Always look at the logcat. See a lecture about memory from Google IO 2011.
Post Ginger it's easier. Since Honeycomb, bitmaps are even counted in your java area. There is no jni area.
Always use recycle for bitmaps you don't need. Don't wait for the GC.
这个问题是在 2010 年提出的,当时 Froyo 还很新鲜。从那以后发生了很多事情。
在3.0之前,位图是在JNI中分配的。内存并没有显示在 Dalvik 统计数据中。它不必再是单一的。
在 2.3 之前,logcat 中不提供 JNI 的内存统计信息(位图解码调用 JNI)。
4.4 疏散更多空间。
5.0 艺术大爆炸。
早在 2010 年,Nexus One 就属于高端设备,容量不到 300MB。应用程序的预算约为 16MB。现在,内存大约是原来的 8 倍。
The question was asked in 2010, when Froyo was fresh. So many things happened since.
Before 3.0, bitmaps were allocated in JNI. The memory didn't show in the Dalvik stats. It doesn't have to be monolithic anymore.
Before 2.3, memory statistics for JNI were not available (bitmap decoding calls JNI) in logcat.
4.4 evacuated more space.
5.0 the big bang of Art.
Back in 2010, Nexus One was high end, with less than 300MB. The budget for an app was around 16MB. Now days, there is about 8 times that memory.