前言
在内存使用过程中使用不当或者超过heap size limit的时候就会出现OOM,那一般OOM 是怎么产生的,会导致什么样的结果呢?
OOM简介
OOM全称为Out of memory,解释为内存溢出。
为了整个Android系统的内存控制需要,Android系统为每一个应用程序都设置了一个硬性的Dalvik Heap Size最大限制阈值,这个阈值在不同的设备上会因为RAM大小不同而各有差异。如果你的应用占用内存空间已经接近这个阈值,此时再尝试分配内存的话,很容易引起OutOfMemoryError的错误。
ActivityManager.getMemoryClass()可以用来查询当前应用的Heap Size阈值(prop dalvik.vm.heapgrowthlimit 也可以),这个方法会返回一个整数,表明你的应用的Heap Size阈值是多少Mb(megabates)。
OOM产生原因
关于Native Heap,Dalvik Heap,Pss等内存管理机制比较复杂,这里不展开描述。简单的说,通过不同的内存分配方式(malloc/mmap/JNIEnv/etc)对不同的对象(bitmap,etc)进行操作会因为Android系统版本的差异而产生不同的行为,对Native Heap与Dalvik Heap以及OOM的判断条件都会有所影响。在2.x的系统上,我们常常可以看到Heap Size的total值明显超过了通过getMemoryClass()获取到的阈值而不会发生OOM的情况,那么针对2.x与4.x的Android系统,到底是如何判断会发生OOM呢?
Android 2.x系统 GC LOG中的dalvik allocated + external allocated + 新分配的大小 >= getMemoryClass()值的时候就会发生OOM。 例如,假设有这么一段Dalvik输出的GC LOG:GC_FOR_MALLOC free 2K, 13% free 32586K/37455K, external 8989K/10356K, paused 20ms,那么32586+8989+(新分配23975)=65550>64M时,就会发生OOM。
Android 4.x系统 Android 4.x的系统废除了external的计数器,类似bitmap的分配改到dalvik的java heap中申请,只要allocated + 新分配的内存 >= getMemoryClass()的时候就会发生OOM,如下图所示(虽然图示演示的是art运行环境,但是统计规则还是和dalvik保持一致)
##如何避免OOM
前面介绍了OOM 的基础知识,那么在实践中有什么方法来减少OOM的出现呢?总结下来大概分下面几个方面:
减小对象的内存占用
内存对象的重复使用
避免对象的内存泄漏
内存使用策略优化
减小对象的内存占用
1、使用更轻量级的数据结构
例如,我们可以考虑使用ArrayMap/SparseArray而不是HashMap等传统数据结构,下图演示了HashMap的简要工作原理,相比起Android系统专门为移动操作系统编写的ArrayMap容器,在大多数情况下,都显示效率低下,更占内存。通常的HashMap的实现方式更加消耗内存,因为它需要一个额外的实例对象来记录Mapping操作。另外,SparseArray更加高效在于他们避免了对key与value的autobox自动装箱,并且避免了装箱后的解箱。
下图是HashMap 的工作原理: