由之前Android sdk文档译文可知,Android设备为了保证多个APP的运行,保证足够RAM的空间,对每个APP的运行时Java堆大小做出了限制,当你的APP申请的JAVA堆空间,超过了设备的Java堆大小
时,系统就会抛出OOM异常,停止当前APP运行。
一、设备的堆大小限制
首先每个设备的堆大小限制是不一样的,你可以通过adb命令getprop dalvik.vm.heapgrowthlimit获取当前设备的堆大小限制,同时,如果你的手机已经root的话,可以通过命令setprop dalvik.vm.heapgrowthlimit 32m(32M数值你可以根据需要设定)来设置当前设备的堆大小限制,这样你就可以使你的APP模拟在不同物理内存的运行。
大家可能在网上之前看过很多文章写Android设备堆大小限制16M,那估计是很早的文章了,现在大部分设备的堆大小限制都在100M以上,以我的手机华为P9为例堆大小已经达到了384M,所以想触发OOM异常也不是这么简单的事了,但是也应当尽量减少堆内存,小心被系统KILL。
二、常见引发OOM的祸源
(1) 内存泄漏
长期持有理应释放对象的引用,会导致堆内存不断累积,从而引发OOM异常,Android大部分内存异常主要由于静态持有外部对象的引用,导致外部对象无法及时释放,以及Android组件或集合对象没有被及时关闭。
(2) 大对象的处理的不当
Android 大的Java对象主要体现在Bitmap对象,因此一定要处理好Bitmap对象,尤其是大量的Bitmap对象的产生。在Android2.3.3(API 10)版本及以下,Bitmap的像素数据都存储在native堆中,只有Bitmap对象本身会存储在Java堆中,而native堆不受设备堆大小的限制,因此影响不大;
但是Android 3.0(API 11)及以上版本,像素数据也会存储在Java堆中,这样如果Bitmap对象频繁申请或者Bitmap对象过大就会导致OOM异常产生。
对于Android2.3.3及以下版本,通过调用recycle()方法就可以来回收native堆内存,但是对于Android 3.0(API 11)及以上版本,由于对象及数据都在Java堆中,所以不能立即回收内存,Android SDK给出的建议就是复用,避免存在大量的Bitmap对象,具体参看官网
其实对于Android开发而言,为了保证高效的内存使用,我们尽可能多的做到复用,避免频繁的申请和是否内存。
三、内存分析Demo
本Demo可以通过不断申请和释放堆内存来查看堆内存的变化,这里堆内存区分了Java堆和Native堆。在这个demo里,你也可以看到Native堆大小不受设备的堆大小的限制,可以达到几个G甚至更大,然而
对于Java堆的申请,当内存总和超过堆大小限制时就会触发OOM异常。另外,当对于Native堆的释放,会立即生效,而Java堆并不会立即释放,即使调用了System.gc(),有时也可能不会释放,得以印证。
代码的github地址:https://github.com/qiaoyanfei/AppMemoryResearch