2019独角兽企业重金招聘Python工程师标准>>>

在编写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下面一楼的回复,让我觉得很雷人啊:

Comment1byromain...@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文档,然后就出来一两篇文章声称是解决某问题的方案,恐怕并不是一种负责任的行为吧。

转载于:https://my.oschina.net/kangchunhui/blog/67627

ANDROID BITMAP内存限制OOM,OUT OF MEMORY相关推荐

  1. Android Bitmap内存限制

    在编写Android程序的时候,我们总是难免会碰到OOM的错误,那么这个错误究竟是怎么来的呢?我们先来看一下这段异常信息: 08-14 05:15:04.764: ERROR/dalvikvm-hea ...

  2. Android避免内存溢出(Out of Memory)方法总结

    避免内存溢出的方法,主要是对以下三个方面对程序进行优化 内存引用 在处理内存引用之前,我们先来复习下什么是强引用.软引用.弱引用.虚引用 强引用:强引用是使用最普遍的引用.如果一个对象具有强引用,那垃 ...

  3. android bitmap内存大小,Android——Bitmap占用的内存大小

    一.作用 知其然 二.概念 1. 几个小概念 像素:组成画面的基本单位,像素没有物理尺寸.可以任意缩放去适配显示屏的像素点大小.不同设备上一个单位像素色块的大小是不一样的 (2)dp 设备独立像素,用 ...

  4. android bitmap oom 最新处理办法,【移动开发】Android中图片过大造成内存溢出,OOM(OutOfMemory)异常解决方法...

    当我们在做项目过程中,一遇到显示图片时,就要考虑图片的大小,所占内存的大小,原因就是Android分配给Bitmap的大小只有8M,试想想我们用手机拍照,普通的一张照片不也得1M以上,所以androi ...

  5. android bitmap.createbitmap内存溢出,android bitmap oom 优化

    android使用位图显示图片,也就是像素点,jpg之类的压缩格式在android都会转成bitmap. 现在手机的分辨率也越来越高,480*800 大小的图片使用的内存大小: 480*800*32/ ...

  6. android 内存分析 郭霖_android 内存泄漏(OOM)问题总结

    对于Java来说,就是new出来的Object 放在Heap上无法被GC回收 Paste_Image.png Context Context Context类本身是一个纯abstract类,它有两个具 ...

  7. [Android] Bitmap OOM解决办法二

    2019独角兽企业重金招聘Python工程师标准>>> 突然出现的Out Of Memory这个BUG导致我们项目中断了好几天,在经过不断地摸索之后,今天终于得到了解决.鉴于其强大的 ...

  8. android应用内存分析,Android应用程序内存分析-Memory Analysis for Android Applications

    Android应用程序内存分析 原文链接:http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html ...

  9. Android复习14【高级编程:推荐网址、抠图片上的某一角下来、Bitmap引起的OOM问题、三个绘图工具类详解、画线条、Canvas API详解(平移、旋转、缩放、倾斜)、矩阵详解】

    目   录 推荐网址 抠图片上的某一角下来 8.2.2 Bitmap引起的OOM问题 8.3.1 三个绘图工具类详解 画线条 8.3.16 Canvas API详解(Part 1) 1.transla ...

最新文章

  1. cdh 安装_CDH的介绍和部署
  2. springAop和AspectJ的关系
  3. ant构建项目迁移到gradle_Gradle构建工具
  4. js中常用的日期处理函数
  5. nginx main error_page
  6. CCF CSP201312-4有趣的数
  7. 大数据可视化技术价值体现在哪方面
  8. C++封装继承多态理解
  9. 旁观者-郑昀博客java调优
  10. 应用之星--完全免费的在线移动开发平台
  11. 分享109个PHP源码,总有一款适合您
  12. 分享一个宝藏级 Java 插件
  13. 编程逻辑性思维的重要性
  14. 【教程】使用 LitJson 处理 Json 数据信息
  15. 物联网+阿里云+小程序开发的一些工具和方法总结
  16. Hadoop学习环境搭建
  17. Revit二开,通过拾取获取面对象
  18. Python核心:5. 容器类型和6.函数function
  19. 阿里云服务器远程连接及账户密码登录
  20. 每个程序猿都应该知道的网站,数量有点多,请点收藏慢慢查看

热门文章

  1. sftp工具都有哪些_色彩校正的工具都有哪些?
  2. java 数据库 空值_java – 是否可以将枚举属性的空值存储到数据库?
  3. 初识Tcl(三):Tcl 变量及运算符
  4. 有关FPGA比较好的文章收藏(备忘)
  5. 用flex进行网易云音乐界面构建和布局解析(2)
  6. 迭代器、生成器、面向过程编程思想
  7. linux学习笔记四:安装lsusb
  8. Naigos PNP图无法加载最新数据
  9. transmit failed error code:65
  10. opencv-python 9.3 图像 ROI