34

我的库超过 600x800 像素 JPEG 时出现 OutOfMemory 异常。


环境

我一直在使用带有 600x800 像素左右的 JPG 图像的图库。

由于我的内容可能比图像更复杂一些,因此我将每个视图设置为将 ImageView 与 JPG 包装在一起的 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 块可以分配?如果是这样,有人可以告诉我在哪里可以看到吗?

非常感谢

迈曼

4

5 回答 5

12

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

您应该注意的一件事:DDMS 显示 java 堆内存消耗。但也有未在 DDMS 中显示的本机内存。据我所知,位图是在本机内存中创建的。所以 DDMS 只是一个不好的工具来跟踪这些内存问题。您只需要确保释放内存,垃圾收集器会在您不再需要图像后收集它们。

垃圾收集器按自己的时间表工作。这就是为什么您应该在不再需要的位图上调用 Bitmap.recycle() 方法。此方法完全释放您用完的本机内存。这样你就不用依赖 GC 了,你可以尽快释放最大的一块内存。

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

这是一篇关于内存分配的好帖子,它可以帮助您更深入地挖掘

于 2010-07-06T09:00:55.207 回答
5

不确定它是否适合您,但是您是否尝试过对图像进行超级采样,将图像加载到位图对象时出现奇怪的内存不足问题

于 2010-07-01T16:41:53.877 回答
0

几周前我也遇到了类似的问题,我通过将图像缩小到最佳点来解决它。我已经在我的博客中编写了完整的方法,并在此处上传了带有 OOM 倾向代码与 OOM 证明代码的完整示例项目

于 2014-06-12T10:47:39.967 回答
0

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

答案应该分为两部分: Pre-Gingerbread:你只是使用小图片,使用二次采样,也许是一张屏幕大小的照片,并希望一切顺利。尝试确保在获取位图之前不要分配无法释放的微小项目。在 Ginger 之前,bmps 的内存必须是连续的,并且不计入 VM 内存。始终查看 logcat。请参阅 Google IO 2011 关于内存的讲座。发布 Ginger 更容易。自 Honeycomb 以来,位图甚至被计入您的 java 区域。没有jni区域。始终对不需要的位图使用回收。不要等待GC。

于 2014-06-13T11:14:37.913 回答
0

这个问题是在 2010 年提出的,当时 Froyo 还很新鲜。从那以后发生了很多事情。在 3.0 之前,位图是在 JNI 中分配的。Dalvik 统计数据中没有显示内存。它不再必须是单片的。在 2.3 之前,JNI 的内存统计信息在 logcat 中不可用(位图解码调用 JNI)。4.4 疏散了更多空间。5.0 艺术大爆炸。早在 2010 年,Nexus One 是高端的,不到 300MB。一个应用程序的预算约为 16MB。现在,大约有 8 倍的内存。

于 2015-05-15T21:11:53.927 回答