android解决bitmap内存溢出之二

最近在做一款塔防游戏,用的事surfaceview框架,由于图片过多,而且游戏过程中都需要这些图片,所以加载成bitmap后造成OOM(out of memory)异常。下面是我一步一步找解决此问题的纪录,再此分享,希望对以后出现此问题的开发者有所帮助。

第一:出现问题,我的测试手机是2。2android操作系统,不会出现oom问题,但是在老板的android4.2上却出现了问题,因为是oom,所以我首先想到的是手动改变手机的内存大小限制。网上有些帖子说可以通过函数设置应用的HEAP SIZE来解决这个问题,其实是不对的。 VMRuntime.getRuntime().setMinimumHeapSize(NewSize); 堆(HEAP)是VM中占用内存最多的部分,通常是动态分配的。堆的大小不是一成不变的,通常有一个分配机制来控制它的大小。比如初始的HEAP是4M大,当4M的空间被占用超过75%的时候,重新分配堆为8M大;当8M被占用超过75%,分配堆为16M大。倒过来,当16M的堆利用不足30%的时候,缩减它的大小为8M大。重新设置堆的大小,尤其是压缩,一般会涉及到内存的拷贝,所以变更堆的大小对效率有不良影响。Max Heap Size,是堆内存的上限值,Android的缺省值是16M(某些机型是24M),对于普通应用这是不能改的。函数setMinimumHeapSize其实只是改变了堆的下限值,它可以防止过于频繁的堆内存分配,当设置最小堆内存大小超过上限值时仍然采用堆的上限值,对于内存不足没什么作用。 setTargetHeapUtilization(float newTarget) 可以设定内存利用率的百分比,当实际的利用率偏离这个百分比的时候,虚拟机会在GC的时候调整堆内存大小,让实际占用率向个百分比靠拢。在手机上进行了多次测试,确实不好使,在此,我断了改变内存限制的方法。

第二:查找出现问题的原因。1,在网上搜索bitmap内存溢出,找到很多说是因为图片大小引起的此问题。观察我的资源文件,没有太大的图片,只是图片数量过多,有将近900张,分别找出一张最大的图片和几张比较大的图片,单独测试,没有发现问题。方法1排除。

2,既然图片数量过多,突破点可能就是图片数量问题。于是分别找了200,400,600图片进行测试,在500左右的时候遇到错误,通过宝哥知道了将小图片整合存放到一张大图的方法,以此来减少图片的数量,但是仔细想想,加载成bitmap的时候还是要切割成小图生成bitmap,所以对此方法表示怀疑。由于以前没用过此方法,试试也无妨。所用到的工具是gdx—texturepackger,它只是一个工具,这里就不多说了。测试的最终结果是还是oom。方法2排除。

3,现在看来,既然不是图片数量的问题,而且会在500张左右的时候报错,那就可能是占用内存大小的问题了,Android手机有内存限制,但是我的图片大小又大于这个限制,这让我头疼了很长时间,研究国外的一些文章,从中发现了一些有用的信息,这些信息能够加深你对Android的解析bitmap机制的理解,在此分享:

As of Honeycomb Bitmap data is allocated in the VM heap.

作为蜂窝位图数据是在VM分配堆。)

There is a reference to it in the VM heap (which is small), but the actual data is allocated in the Native heap by the underlying Skia graphics library. 有一个引用在VM堆(小),但实际的数据是在本机堆分配由底层Skia图形库。
Unfortunately, while the definition of BitmapFactory.decode…() says that it returns null if the image data could not be decoded, the Skia implementation (or rather the JNI glue between the Java code and Skia) logs the message you’re seeing (“VM won’t let us allocate xxxx bytes”) and then throws an OutOfMemory exception with the misleading message “bitmap size exceeds VM budget”. 不幸的是,虽然BitmapFactory.decode的定义…()表示,它返回null如果图像数据不能解码,Skia实现(或者说JNI胶之间的Java代码和Skia)日志消息你看到(“VM不会让我们分配xxxx字节”),然后抛出一个OutOfMemory异常与误导信息”位图的大小超过VM预算”。
The issue is not in the VM heap but is rather in the Native heap. 这个问题不是在VM堆而是在本机堆。
The Natïve heap is shared between running applications, so the amount of free space depends on what other applications are running and their bitmap usage. 本机堆是正在运行的应用程序之间共享,因此空闲空间的大小取决于其他运行程序,他们使用的位图。

However, I have found that getNativeHeapFreeSize() and getNativeHeapSize() are not reliable. 然而,我发现getNativeHeapFreeSize()和getNativeHeapSize()是不可靠的。

The Native heap size varies by platform. 本机堆大小不同的平台。
So at startup, we check the maximum allowed VM heap size to determine the maximum allowed Native heap size. 所以在启动时,我们检查最大允许VM堆大小来确定最大允许本机堆大小。

“Bitmap data is not allocated in the VM heap” — it is allocated on the VM heap as of Honeycomb “位图数据不是在VM分配堆”——这是VM分配的堆在蜂窝Yes. 是的。 As of Honeycomb (v3.0), bitmap data is allocated on the VM heap. 作为蜂窝(v3.0),位图数据堆上分配VM。 So all of the above only applies to Gingerbread (v2.3.x) and before 所以所有上述只适用于姜饼(v2 3 x)和之前

这些信息零零散散,但是不难发现,问题的原因就在于根据Android版本的不同,bitmap data存放的位置是不同的,3.0以前是分配在native heap上,3.0以后是分配在VM heap上。

为了验证这个问题,我们需要抓去heap快照,众所周知,eclipse中的ddms可以查看heap信息,但是不够全面,这里我用到了adb shell dumpsys meminfo+包名 这条命令来查看heap信息,对比两个机子的不同如下:

2.2的

4.0

从中不难发现,bitmap的存放位置根据Android版本的不同真的有所不同。好了,下面就是找出怎么把图片存放到native heap里就行了,BitmapFactory里就那么几个decode方法,很容易找到BitmapFactory .decodeStream就可以解决。下面贴一下代码:

BitmapFactory.Options options = new BitmapFactory.Options();

options.inPreferredConfig = Config.ARGB_8888;

options.inPurgeable = true;// 允许可清除

options.inInputShareable = true;// 以上options的两个属性必须联合使用才会有效果

String sname = String.format( “xxx.png”, sTowerStyle, j, sDirction, i);

InputStream is = am.open(sname);

arrBmp[ iBmpIndex] = BitmapFactory .decodeStream(is, null, options);

ok搞定手工。

小问题大发现:1.遇到问题,不要急躁。最初遇到这个问题的时候以为很好解决,试了几种方法后还是解决不了,内心难免会有挫败感,这个时候,最需要的是耐心。

2.网上有很多资源,但是能不能查得到就是自己的问题了,我发现那些编程老手们查找问题总是能够准确定位,快速的找到解决方法。以后要加强这方面的锻炼。

3.国内的资源大多偏向解决问题,国外的资源大多偏向分析问题,所以有的时候还是需要多看看外文的一些资料。当然这需要不错的英文功底。当初看外文的资料,头都大了。这是需要加强的一个方面。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值