现在的位置: 首页 > 综合 > 正文

960000-byte external allocation too large for this process VM won’t let us allocate 960000 bytes java.lang.OutOfMemoryError: bit

2018年01月09日 ⁄ 综合 ⁄ 共 8244字 ⁄ 字号 评论关闭

出现问题:

11-30 01:59:13.126: INFO/Kingdom(385): set status:STOP
11-30 01:59:13.174: INFO/Kingdom(385): WINDOW_WIDTH:320 WINDOW_HEIGHT:290
11-30 01:59:13.184: ERROR/dalvikvm-heap(385): 960000-byte external allocation too large for this process.
11-30 01:59:13.194: ERROR/(385): VM won't let us allocate 960000 bytes
11-30 01:59:13.206: DEBUG/skia(385): --- decoder->decode returned false
11-30 01:59:13.215: DEBUG/AndroidRuntime(385): Shutting down VM
11-30 01:59:13.215: WARN/dalvikvm(385): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
11-30 01:59:13.215: ERROR/AndroidRuntime(385): Uncaught handler: thread main exiting due to uncaught exception
11-30 01:59:13.245: ERROR/AndroidRuntime(385): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:447)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:323)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:346)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at com.haypi.kingdom.views.CityView.onMeasure(CityView.java:356)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.View.measure(View.java:7964)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3023)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:888)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.widget.LinearLayout.measureVertical(LinearLayout.java:350)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.widget.LinearLayout.onMeasure(LinearLayout.java:278)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.View.measure(View.java:7964)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3023)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:245)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.View.measure(View.java:7964)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3023)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:245)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.View.measure(View.java:7964)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.ViewRoot.performTraversals(ViewRoot.java:950)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1633)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.os.Looper.loop(Looper.java:123)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at android.app.ActivityThread.main(ActivityThread.java:4363)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at java.lang.reflect.Method.invokeNative(Native Method)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at java.lang.reflect.Method.invoke(Method.java:521)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
11-30 01:59:13.245: ERROR/AndroidRuntime(385):     at dalvik.system.NativeStart.main(Native Method)
11-30 01:59:13.274: INFO/Process(52): Sending signal. PID: 385 SIG: 3

问题解析:

 http://code.google.com/p/android/issues/detail?id=3405

 http://code.google.com/p/android/issues/detail?id=8488

上面两个网址是提交的有关这方面的错误

从网上找到某人关于这方面的讲解 贴下来:

我们从上面的异常堆栈信息中,可以看出是在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文档,然后就出来一两篇文章声称是解决某问题的方案,恐怕并不是一种负责任的行为吧。

 

http://bbfar.com.cn/article/79/2656$3.html

在Android平台下实现OpenGL ES程序的贴图加载操作一般是通过BitmapFactory.decodeResource这个api,然后用系统封装好的 GLUtils.texImage2D函数直接转换为gl贴图即可,方便快捷。但在较新版的Android系统中res中的图片文件夹根据dpi设备分辨率的不同,细分了很多文件夹处理以支持不同设备的分辨率加载对应的图片,如drawable-hdpi,drawable-ldpi,drawable- mdpi等,如果没有注意这个问题而将贴图图片随意安置的话,在decode的时候系统会默认根据设备dpi的不同对目标图片格式解码的同时进行大小调整,也就是说有可能破坏原本已经是2^n大小的贴图图片,导致原本在模拟器上正确的绘图在真机上变成大白板!   解决这个问题的方法可以将图片放到不受dpi影响的drawable-nodpi中,或者设置BitmapFactory的选项,不处理dpi相关问题。 http://blogs.sonyericsson.com/developerworld/2010/05/18/android-one-finger-zoom-tutorial-part-1/

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wbw1985/archive/2010/11/30/6044724.aspx

抱歉!评论已关闭.