[转贴] 最近在作一款塔防游戏,用的事surfaceview框架,因为图片过多,并且游戏过程当中都须要这些图片,因此加载成bitmap后形成OOM(out of memory)异常。下面是我一步一步找解决此问题的纪录,再此分享,但愿对之后出现此问题的开发者有所帮助。android
第一:出现问题,个人测试手机是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的时候调整堆内存大小,让实际占用率向个百分比靠拢。在手机上进行了屡次测试,确实很差使,在此,我断了改变内存限制的方法。shell
第二:查找出现问题的缘由。1,在网上搜索bitmap内存溢出,找到不少说是由于图片大小引发的此问题。观察个人资源文件,没有太大的图片,只是图片数量过多,有将近900张,分别找出一张最大的图片和几张比较大的图片,单独测试,没有发现问题。方法1排除。编程
2,既然图片数量过多,突破点可能就是图片数量问题。因而分别找了200,400,600图片进行测试,在500左右的时候遇到错误,经过宝哥知道了将小图片整合存放到一张大图的方法,以此来减小图片的数量,可是仔细想一想,加载成bitmap的时候仍是要切割成小图生成bitmap,因此对此方法表示怀疑。因为之前没用过此方法,试试也无妨。所用到的工具是gdx—texturepackger,它只是一个工具,这里就很少说了。测试的最终结果是仍是oom。方法2排除。app
3,如今看来,既然不是图片数量的问题,并且会在500张左右的时候报错,那就多是占用内存大小的问题了,Android手机有内存限制,可是个人图片大小又大于这个限制,这让我头疼了很长时间,研究国外的一些文章,从中发现了一些有用的信息,这些信息可以加深你对Android的解析bitmap机制的理解,在此分享:框架
As of Honeycomb Bitmap data is allocated in the VM heap.eclipse
做为蜂窝位图数据是在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堆大小来肯定最大容许本机堆大小。spa
“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.国内的资源大多偏向解决问题,国外的资源大多偏向分析问题,因此有的时候仍是须要多看看外文的一些资料。固然这须要不错的英文功底。当初看外文的资料,头都大了。这是须要增强的一个方面。