转载自:http://www.7dot9.com/2010/08/android-bitmap%e5%86%85%e5%ad%98%e9%99%90%e5%88%b6/
在编写Android程序的时候,我们总是难免会碰到OOM的错误,那么这个错误究竟是怎么来的呢?我们先来看一下这段异常信息:
08-14 05:15:04.764: ERROR/dalvikvm-heap(264): 3528000-byte external allocation too large for this process.
08-14 05:15:04.764: ERROR/(264): VM won’t let us allocate 3528000 bytes
08-14 05:15:04.764: DEBUG/skia(264): — decoder->decode returned false
08-14 05:15:04.774: DEBUG/AndroidRuntime(264): Shutting down VM
08-14 05:15:04.774: WARN/dalvikvm(264): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
08-14 05:15:04.774: ERROR/AndroidRuntime(264): Uncaught handler: thread main exiting due to uncaught exception
08-14 05:15:04.794: ERROR/AndroidRuntime(264): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:447)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:346)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:372)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at com.xixun.test.HelloListView.onCreate(HelloListView.java:33)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2459)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2512)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.access$2200(ActivityThread.java:119)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1863)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.os.Handler.dispatchMessage(Handler.java:99)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.os.Looper.loop(Looper.java:123)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at android.app.ActivityThread.main(ActivityThread.java:4363)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at java.lang.reflect.Method.invokeNative(Native Method)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at java.lang.reflect.Method.invoke(Method.java:521)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
08-14 05:15:04.794: ERROR/AndroidRuntime(264): at dalvik.system.NativeStart.main(Native Method)
从上面这段异常信息中,我们看到了一个OOM(OutOfMemory)错误,我称其为(OMG错误)。出现这个错误的原因是什么呢?为什么解码图像会出现这样的问题呢?关于这个问题,我纠结了一段时间,在网上查询了很多资料,甚至查看了Android Issues,确实看到了相关的问题例如Issue 3405,Issue 8488,尤其Issue 8488下面一楼的回复,让我觉得很雷人啊:
Comment 1 by romain…@android.com, May 23, 2010
Your app needs to use less memory.
当然我们承认不好的程序总是程序员自己错误的写法导致的 ,不过我们倒是非常想知道如何来规避这个问题,那么接下来就是解答这个问题的关键。
我们从上面的异常堆栈信息中,可以看出是在BitmapFactory.nativeDecodeAsset(),对应该方法的native方法是在BitmapFactory.cpp中的doDecode()方法,在该方法中申请JavaPixelAllocator对象时,会调用到Graphics.cpp中的setJavaPixelRef()方法,在setJavaPixelRef()中会对解码需要申请的内存空间进行一个判断,代码如下:
bool r = env->CallBooleanMethod(gVMRuntime_singleton,
gVMRuntime_trackExternalAllocationMethodID,
jsize);
而JNI方法ID — gVMRuntime_trackExternalAllocationMethodID对应的方法实际上是dalvik_system_VMRuntime.c中的Dalvik_dalvik_system_VMRuntime_trackExternalAllocation(),而在该方法中又会调用大HeapSource.c中的dvmTrackExternalAllocation()方法,继而调用到externalAllocPossible()方法,在该方法中这句代码是最关键的
heap = hs2heap(hs);
currentHeapSize = mspace_max_allowed_footprint(heap->msp);
if (currentHeapSize + hs->externalBytesAllocated + n <=
heap->absoluteMaxSize)
{
return true;
}
这段代码的意思应该就是当前堆已使用的大小(由currentHeapSize和hs->externalBytesAllocated构成)加上我们需要再次分配的内存大小不能超过堆的最大内存值。那么一个堆的最大内存值究竟是多大呢。通过下面这张图,我们也许可以看到一些线索(自己画的,比较粗糙)
最终的决定权其实是在Init.c中,因为Android在启动系统的时候会去优先执行这个里面的函数,通过调用dvmStartup()方法来初始化虚拟机,最终调用到会调用到HeapSource.c中的dvmHeapSourceStartup()方法,而在Init.c中有这么两句代码:
gDvm.heapSizeStart = 2 * 1024 * 1024; // Spec says 16MB; too big for us.
gDvm.heapSizeMax = 16 * 1024 * 1024; // Spec says 75% physical mem
在另外一个地方也有类似的代码,那就是AndroidRuntime.cpp中的startVM()方法中:
strcpy(heapsizeOptsBuf, "-Xmx");
property_get("dalvik.vm.heapsize", heapsizeOptsBuf+4, "16m");
//LOGI("Heap size: %s", heapsizeOptsBuf);
opt.optionString = heapsizeOptsBuf;
同样也是默认值为16M,虽然目前我看到了两个可以启动VM的方法,具体Android何时会调用这两个初始化VM的方法,还不是很清楚。不过可以肯定的一点就是,如果启动DVM时未指定参数,那么其初始化堆最大大小应该就是16M,那么我们在网上查到了诸多关于解码图像超过8M就会出错的论断是如何得出来的呢?
我们来看看HeapSource.c中的这个方法的注释
/*
* External allocation tracking
*
* In some situations, memory outside of the heap is tied to the
* lifetime of objects in the heap. Since that memory is kept alive
* by heap objects, it should provide memory pressure that can influence
* GCs.
*/
static bool
externalAllocPossible(const HeapSource *hs, size_t n)
{
const Heap *heap;
size_t currentHeapSize;
/* Make sure that this allocation is even possible.
* Don’t let the external size plus the actual heap size
* go over the absolute max. This essentially treats
* external allocations as part of the active heap.
*
* Note that this will fail "mysteriously" if there’s
* a small softLimit but a large heap footprint.
*/
heap = hs2heap(hs);
currentHeapSize = mspace_max_allowed_footprint(heap->msp);
if (currentHeapSize + hs->externalBytesAllocated + n <=
heap->absoluteMaxSize)
{
return true;
}
HSTRACE("externalAllocPossible(): "
"footprint %zu + extAlloc %zu + n %zu >= max %zu (space for %zu)\n",
currentHeapSize, hs->externalBytesAllocated, n,
heap->absoluteMaxSize,
heap->absoluteMaxSize -
(currentHeapSize + hs->externalBytesAllocated));
return false;
}
标为红色的注释的意思应该是说,为了确保我们外部分配内存成功,我们应该保证当前已分配的内存加上当前需要分配的内存值,大小不能超过当前堆的最大内存值,而且内存管理上将外部内存完全当成了当前堆的一部分。也许我们可以这样理解,Bitmap对象通过栈上的引用来指向堆上的Bitmap对象,而Bitmap对象又对应了一个使用了外部存储的native图像,实际上使用的是byte[]来存储的内存空间,如下图:
我想到现在大家应该已经对于Bitmap内存大小限制有了一个比较清楚的认识了。至于前几天从Android123上看到“Android的Btimap处理大图片解决方法”一文中提到的使用BitmapFactory.Options来设置inTempStorage大小,我当时看完之后就尝试了一下,这个设置并不能解决问题,而且很有可能会给你带来不必要的问题。从BitmapFactory.cpp中的代码来看,如果option不为null的话,那么会优先处理option中设置的各个参数,假设当前你设置option的inTempStorage为1024*1024*4(4M)大小的话,而且每次解码图像时均使用该option对象作为参数,那么你的程序极有可能会提前失败,在我的测试中,我使用了一张大小为1.03M的图片来进行解码,如果不使用option参数来解码,可以正常解码四次,也就是分配了四次内存,而如果我使用option的话,就会出现OOM错误,只能正常解码两次不出现OOM错误。那么这又是为什么呢?我想是因为这样的,Options类似与一个预处理参数,当你传入options时,并且指定临时使用内存大小的话,Android将默认先申请你所指定的内存大小,如果申请失败,就抛出OOM错误。而如果不指定内存大小,系统将会自动计算,如果当前还剩3M空间大小,而我解码只需要2M大小,那么在缺省情况下将能解码成功,而在设置inTempStorage大小为4M的情况下就将出现OOM错误。所以,我个人认为通过设置Options的inTempStorage大小根本不能作为解决大图像解码的方法,而且可能带来不必要的问题,因为OOM错误在某些情况是必然出现的,也就是上面我解释的那么多关于堆内存最大值的问题,只要解码需要的内存超过系统可分配的最大内存值,那么OOM错误必然会出现。当然对于Android开发网为何发布了这么一篇文章,个人觉得很奇怪,我想作为一个技术人员发布一篇文章,至少应该自己尝试着去测试一下自己的程序吧,如果只是翻翻SDK文档,然后就出来一两篇文章声称是解决某问题的方案,恐怕并不是一种负责任的行为吧。
=================================
还是点到为止吧,希望大家都自己去测试一下,验证一下,毕竟自己做过验证的才能算是放心的。
- 大小: 71.4 KB
- 大小: 14 KB
分享到:
相关推荐
文档标题和描述中提到的“ANDROIDBITMAP内存限制OOM,OUTOFMEMORY”指的就是在处理位图(BITMAP)时超出了虚拟机(VM)的内存预算,导致系统抛出OutOfMemoryError异常。 根据给出的内容部分,我们可以推断出以下知识...
在Android开发中,处理`Bitmap`内存溢出问题是一个常见的挑战,尤其是在处理高分辨率或大尺寸图片时。当应用程序尝试加载或操作一张超出虚拟机内存预算的`Bitmap`时,系统会抛出`java.lang.OutOfMemoryError: bitmap...
本文将深入探讨Android Bitmap内存限制以及如何避免OOM错误。 首先,我们需要理解引发上述错误的原因。当Android系统尝试分配一块超过其当前可用内存大小的内存时,会抛出`java.lang.OutOfMemoryError: bitmap size...
内存溢出(Out Of Memory,简称OOM)通常发生在应用消耗的物理内存超过了系统分配给它的最大内存限制。对于Android而言,当一个进程使用的本机或Java堆内存超过了一定阈值时,就会触发OOM错误。`Bitmap`作为占用内存...
在Java(包括Android)中,每个应用都有一定的内存限制。当应用尝试分配的内存超过其可用的堆内存时,就会抛出此错误。Bitmap对象是直接存储在堆内存中的,因此大量或者大尺寸的Bitmap会迅速消耗掉内存。 解决`...
6. **Bitmap内存管理与优化** 处理Bitmap时,需要考虑内存占用。大型Bitmap可能导致内存溢出。可以通过`Options`对象设置解码时的宽高限制,使用`inSampleSize`降低解码后的图像分辨率,从而减少内存消耗。此外,...
特别是在处理大尺寸图片时,由于内存限制,系统可能无法分配足够的内存,从而导致应用崩溃。"处理android bitmap oom 2.0版本关闭了硬件加速"这一主题,关注的是如何在Android 2.0版本及其后续版本中避免Bitmap导致...
在Android开发中,Bitmap对象是用于处理图像的主要类,但如果不妥善管理,它可能会引发“Out Of Memory”(OOM)错误。...记住,每个应用都有其特定的内存限制,理解并适应这些限制是成功避免OOM的关键。
理解并熟练运用这些知识点,开发者可以有效地管理Bitmap内存,避免因图像处理导致的内存问题,从而提高应用性能和用户体验。在实际开发中,还需要关注Android系统的版本差异,因为不同的Android版本可能有不同的...
这是因为Android设备的内存有限,尤其是当加载大图或大量图片时,容易超出可用内存限制。"处理android bitmap oom 2.0版本"是一个针对这个问题的解决方案更新,它旨在修复前一版本中回收Bitmap对象可能导致的问题。 ...
Bitmap占用大量内存,因此在Android中管理Bitmap内存至关重要。可以使用`options.inSampleSize`来降低图片质量,减少内存占用。此外,使用`BitmapFactory.Options`的`inPurgeable`和`inInputShareable`属性可以允许...
Android设备的默认最大堆内存限制通常是16MB,但不同设备可能有所不同。 - **栈(Stack)**:栈内存用于存储基本类型变量(如int、float等)、对象引用和方法调用时的局部变量。栈内存分配速度快,但由于其大小和...
- 考虑到Android设备的内存限制,转换后的Bitmap应使用适当配置,如调整大小、选择适当的压缩格式(如ARGB_8888或RGB_565)等,以减少内存消耗。 - 使用异步操作,避免UI线程阻塞,可以使用AsyncTask或其他异步...
内存溢出(OutOfMemoryError)是Android开发中常见的问题,特别是在处理大尺寸的位图(Bitmap)时。由于Android设备的内存有限,长时间持有资源的引用会导致内存无法被回收,从而引发内存泄露。内存泄露不仅浪费资源...
Android提供了多种方法来创建和加载Bitmap,例如从资源文件、文件系统、网络或内存中。 在Activity间传递Bitmap,有两种主要方式:Intent extras和使用Parcelable接口。由于Bitmap对象较大,直接通过Intent的extras...
- 开发者在实际应用中,需要根据具体设备的性能和内存限制,对工具类进行充分的测试和调整,确保在各种情况下都能稳定运行。 综上所述,处理Android中的图片内存溢出问题需要综合运用多种策略,结合特定场景进行...
在Android开发中,图片下载和显示是常见的操作,但如果不恰当处理,很容易引发内存溢出(Out of Memory,简称OOM)问题。内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,导致程序崩溃。对于Android应用...
1. **了解内存限制**: Android系统对每个应用分配一定的内存空间,这个值在不同设备上会有所不同。了解目标设备的内存规格有助于设定合理的内存管理策略。 2. **使用轻量级数据结构**: 在处理数据时,优先选择...
- 考虑到Android平台的资源限制,一个良好的图片处理库应该具备性能优化。此库可能采用了内存管理策略,如缓存策略,以减少内存消耗和提高处理速度。 7. **兼容性**: - 作为一个成熟的Android库,它应该兼容广泛...