我正在android 3.1上测试,大堆大小选项,大约有250万可用内存。
我将以下代码设置为每当我按下应用程序首选项中的测试按钮时运行:

float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
bm.recycle();
bm  = null;
foo = null;

我有足够的记忆——我可以按几次按钮,没有问题。
但如果我继续按这个按钮,最终(不到20次点击)它会在内存不足的情况下死亡。[通常在android.graphics.bitmap.nativecreate(本机方法)]
没有别的事情发生了——我从不需要离开优先活动。当我点击按钮时,也会显示一个小的祝酒词,所以其他少量的用户界面活动正在进行。
这是因为碎片,还是仅仅是Android位图代码和/或GC中的一个可怕的错误?或者我只是在做傻事?(请让它变得愚蠢…)
有人有解决办法吗?因为上面的代码相当代表了每次用户调用它时我的代码必须做的事情,而且现在尽管仔细地清除了一些变量,但在使用了几次之后,它还是死掉了。(这已经让我发疯很久了!)
[更新]
我已经确认这是一个碎片问题或gc bug,因为堆转储显示,在处理过程中,当空闲(没有泄漏)在大约26m达到峰值时,我只使用5.6m。(同时,本地堆保持在4m以下),而Java堆同时扩展到我的测试设备上的280M限制,此时我开始获得内存异常。因此,我在峰值时只使用了可用堆的10%,但内存不足。
[添加对System.gc()的调用很不幸地修复了我上面给出的简单测试用例。我说不幸是因为(a)它不应该有什么区别,(b)因为我已经在真正的代码中定期调用它,所以它意味着我上面的简单测试用例太简单了。]
还有人碰到这个吗?有什么解决办法吗?有没有一个优雅的方法来重新启动我的应用程序?
[更新]
以下版本可靠地导致3到4次调用(按下按钮)内存不足:
float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
int []bar = new int[3*2048*2048];
bm.recycle();
bm = null;
System.gc();
foo = null;
System.gc();
bar = null;
System.gc();

内存跟踪显示堆每次调用都在稳定增长,直到达到极限并死亡。如果我去掉这三个分配中的任何一个,它就会达到平衡并无限期地存活。除去最后一个gc()之外的所有内容,会使它更快地死亡。
我会说这是一个碎片问题,本身不是GC错误。如果有人知道如何修理,请告诉我。int[]分配用于写入位图,因此我没有将其分配为二维数组的选项(Android位图库的限制)。

最佳答案

下面是另一个针对这个问题的解决方案页面:
Strange out of memory issue while loading an image to a Bitmap object
具体来说,以法莲的回答(节选):
“1)每次执行bitmapFactory.decodeXYZ()时,请确保传入bitmapFactory.Options,其中inpurgable设置为true(最好是ininputshareable也设置为true)。
“2)切勿使用bitmap.createbittmap(width,height,config.argb_8888)。我是说永远不会!我从来没有经历过这样的事情,在经历了几次记忆错误之后。没有多少recycle(),system.gc(),无论有什么帮助。它总是引发异常。另一种实际工作的方法是在您的抽屉中放置一个虚拟图像(或使用上面的步骤1解码的另一个位图),将其重新缩放到您想要的任何位置,然后操作生成的位图(例如将其传递到画布上以获得更多乐趣)。所以,您应该改为使用:bitmap.createScalledBitmap(srcBitmap,width,height,false)。如果出于任何原因,您必须使用蛮力创建方法,那么至少要传递config.argb_4444。“
在评论中,有人说这解决了他们的问题,这和这里的操作非常相似。
我要补充的是,DianeHackborn评论说,从3.0版开始,android不再从本地堆分配位图,而是直接从常规堆分配位图。这可能会使您的本地堆数字无关紧要。参见本页的Hackbod评论:
Bitmaps in Android
我想这意味着一个相当大的变化,如蜂窝关于位图分配,所以这可以解释为什么有缺陷与这种分配(如果有的话)。我不知道这个更改对recycle()命令有什么影响,但是根据以法莲上面的评论,答案可能“不是很好”。
最后,
使用largeheap来摄取巨大的位图可能会被视为对其他应用程序不太好,尤其是当您接近设备的物理极限时。我不确定如何避免这种情况,但是当你的应用程序在其他应用程序上运行时,要准备好进行大量的onpause()/onresume()活动,而它们又回到了你的应用程序上。这个问题的答案包括一个讨论:
Detect application heap size in Android

07-24 09:53
查看更多