Android Bitmap内存限制

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 3405Issue 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()方法中:

[indent]strcpy(heapsizeOptsBuf, “-Xmx”);

property_get(“dalvik.vm.heapsize”, heapsizeOptsBuf+4, “16m”);

ntime.cpp中的startVM()方法中:

[indent]strcpy(heapsizeOptsBuf, “-Xmx”);

property_get(“dalvik.vm.heapsize”, heapsizeOptsBuf+4, “16m”);

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值