Android Out Of Memory(OOM) 的详细研究

来源:互联网 发布:西电软件学院 编辑:程序博客网 时间:2024/06/08 10:39
  基于Android开发多媒体和游戏应用时,可能会挺经常出现Out Of Memory 异常 ,顾名思义这个异常是说你的内存不够用或者耗尽了。    在Android中,一个Process 只能使用16M内存,如果超过了这个限制就会跳出这个异常。这样就要求我们要时刻想着释放资源。Java的回收工作是交给GC的,如何让GC能及时的回收已经不是用的对象,这个里面有很多技巧,大家可以google一下。    因为总内存的使用超过16M而导致OOM的情况,非常简单,我就不继续展开说。值得注意的是Bitmap在不用时,一定要recycle,不然OOM是非常容易出现的。    本文想跟大家一起讨论的是另一种情况:明明还有很多内存,但是发生OOM了。    这种情况经常出现在生成Bitmap的时候。有兴趣的可以试一下,在一个函数里生成一个13m 的int数组。    再该函数结束后,按理说这个int数组应该已经被释放了,或者说可以释放,这个13M的空间应该可以空出来,    这个时候如果你继续生成一个10M的int数组是没有问题的,反而生成一个4M的Bitmap就会跳出OOM。这个就奇怪了,为什么10M的int够空间,反而4M的Bitmap不够呢?   这个问题困扰很久,在网上,国外各大论坛搜索了很久,一般关于OOM的解释和解决方法都是,如何让GC尽快回收的代码风格之类,并没有实际的支出上述情况的根源。   直到昨天在一个老外的blog上终于看到了这方面的解释,我理解后归纳如下:   在Android中:   1.一个进程的内存可以由2个部分组成:java 使用内存 ,C 使用内存 ,这两个内存的和必须小于16M,不然就会出现大家熟悉的OOM,这个就是第一种OOM的情况。   2.更加奇怪的是这个:一旦内存分配给Java后,以后这块内存即使释放后,也只能给Java的使用,这个估计跟java虚拟机里把内存分成好几块进行缓存的原因有关,反正C就别想用到这块的内存了,所以如果Java突然占用了一个大块内存,即使很快释放了:    C能使用的内存 = 16M - Java某一瞬间占用的最大内存。   而Bitmap的生成是通过malloc进行内存分配的,占用的是C的内存,这个也就说明了,上述的4MBitmap无法生成的原因,因为在13M被Java用过后,剩下C能用的只有3M了。

下面是我参考的blog的所有内容:
[android-developers] Re: OOM error caught bei DefaultException handler … but there is plenty of memory
内如如下:

You might try to pre-allocate bitmap memory before launching the WebViews?It’s not the WebView that’s triggering the OOM, but some arbitrary otherpiece of code that needs memory that is not there anymore. Very often thisis happening when starting a new activity.

Ok, I see, I have to start dealing with automating my apology.

There is one more, small thing that I can do. I also do some downloading andXML parsing in the background at times. This only takes Java Heap (<3MB),but maybe I should move that stuff to a separate process. This may lower thechances of an OOM. I’ll think about it, but with all the added complexity ofinter process communication I am not sure I would want to go there.

Anyway, thanks for sharing your insights. That was very helpful.

On Wed, Oct 7, 2009 at 10:48 PM, Tom Gibara <[EMAIL PROTECTED]> wrote:

I think it’s better to add a couple more columns to the table to see the
picture (as I see it) more clearly:> JH = Java Heap
JU = Memory actually used by Java
NH = Native Heap
TU = Total memory Used = JU + NH
TA = Total memory Allocated = JH + NH

(note I’m not distinguishing between native heap and native heap used
because it’s not relevant here)

The system requires TA (the process size as you called it) to not exceed
16MB, the evolution is:

JU JH NH TU TA
1) 2 2 0 2 2
2) 4 4 0 4 4
3) 4 4 2 6 6
4) 14 14 2 16 16
5) 4 14 2 6 16
6) 4 14 4 10 18 * OH NO! *

The key is what happens between (4) and (5): GC reclaims 10MB (JU reduced> by 10MB) but the java heap doesn’t shrink (JH stays at 14MB). This enlarged > java heap basically squeezes the maximum native heap allocation.

The simplest approach is to try and ensure that your application maintains
a ‘flatish’ memory profile - no big spikes. You should do this anyway, since
it means that your application is being well behaved and won’t force other
apps to be terminated just because your application needs a temporary shot> of memory (which will then remain as a glut until the application restarts).

As you point out, WebViews are heavy on memory usage, and these might be
what’s causing your memory usage to spike. I don’t have any good suggestions
for a fix. You might try to pre-allocate bitmap memory before launching the
WebViews? It might work, but it may be complicated to do and could cause
OOMs when WebViews are instantiated - no way around that, your application
is simply using too much memory at that point.

2012年02月04日 更新:

16M是怎么来的?算是实验来的吧,每个机型不一样,模拟器不同版本也不一样,可以通过:
Runtime.getMaxMemory() 来查看。

long maxMemory()
Returns the maximum amount of memory that may be used by the virtual machine, or Long.MAX_VALUE if there is no such limit.

0 0