Android内存管理及OOM浅析

一、Android内存管理

       1、 在Android系统中,为了让应用程序能够高效的运行,每个应用程序都会由独立的Dalvic虚拟机实例来运行,它是由Zygote服务进程演变而来,也就是说每个应用程序都是在属于自己的进程中运行的。一方面,如果程序在运行过程中出现内存泄漏的问题,仅仅会使得自己所在的进程被系统Kill掉而不会影响其他的进程(若系统进程出现问题,会导致系统崩溃,出现重启)。另一方面,Android为不同类型的进程分配了不同的内存使用上限,如果应用使用的内存超过了这个上线会被系统视为内存泄漏(OOM:OutOfMemory),会被系统kill掉。

       2、分配与回收内存

         (1)每个进程的Dalvic HEAP 反映了使用内存的占用范围,可以根据需要进行增长,但是有一个系统上限。

         (2)系统会在新的内存分配之前判断出heap的尾端剩余空间是否足够,不够就会触发GC(Garbage Collection)机制,从而腾出更多空闲的内存空间。GC机制,也就是所谓的垃圾回收机制,Android会在适当的时候触发GC机制,将一些不再使用的对象回收从而释放出更多的内存空间。

         (3)查看本机heapSize:

ActivityManager manager = (Activity)getSystemService(Context.ACTIVITY_SERVICE);

int heapSize = manager.getMemoryClass();

 二、OOM

    在上文中,可知由于程序在运行过程中使用的内存量大于系统为其分配的内存时会发生OOM!

    那么在实际的编码过程中,如何避免OOM呢?

   1、尽量减少新分配出来的对象占用内存的大小,尽量使用更加轻量的对象

          (1)使用更加轻量的数据结构,比如用ArrayMap、SparseArray等去替代传统的HashMap等数据结构

          (2)避免在安卓中使用枚举,因为枚举通常会比使用静态常量要消耗两倍以上的内存

          (3)减少Bitmap对象的内存占用:读取一个bitmap图片时,不要去加载不需要的分辨率。因为将一张图片解析成一个bitmap对象时所占用的内存并不是这个图片在硬盘中的大小,可能一张图片在硬盘中只有100k,但是在读取到内存中是按照像素点计算的,假如该图片是1500*1000像素,颜色类型为ARGB_8888,则每个像素点会占用4个字节,总内存就是1500*1000*4字节,也就是5.7M,这就比较大了。用以下两种方法解决:

                  A、inSampleSize:缩放比例,在图片载入内存前,计算出一个合适的比例,防止大图载入

                  B、decodeFormat:解码格式,选择ARGB_8888/RBG_565/ARGB_4444/ALPHA_8,解码格式有差异

         (4)使用更小的图片:防止在xml加载资源时初始化试图因为内存不足而发生InflationException,其根本原因就是发生OOM。

   2、内存对象的重复利用

         Android中最常用的缓存算法LRU(Least Recently Use)

       (1)复用系统自带的资源,比如字符串、图片、动画、样式、颜色等

       (2)ListView和GridView中大量重复子组件的视图里面对convertView的复用

       (3)Bitmap对象的复用

       (4)避免在onDraw()方法里面执行对象的创建

       (5)使用StringBuild代替“+”

  3、避免对象的内存泄漏

       内存对象的泄露会导致不再使用的对象无法及时释放,浪费内存空间,易造成OOM

       (1)注意Activity的泄露

              Activity泄露最为严重,涉及内存多,影响面广。有以下两种情形:

             A、内部类引用导致Activity泄露。典型的是Handler导致的Activity泄露。如果Handler中有延迟的任务或者等待执行的任务队列过长,就很可能因为Handler引起Activity的泄露。

             B、Activity Context被传递到其他实例中,可能导致自身被引用而发生内存泄露

       (2)使用Application Context代替Activity Context

              Dialog是需要用Activity的Context,除此以外使用Application Context

       (3)临时Bitmap的及时收回

              bitmap是一个占用内存的大胖子

       (4)监听器撤销:listener等

       (5)缓存容器的对象泄露:清除容器中的对象

       (6)webview的泄露

       (7)注意cursor对象是否关闭

三、内存的优化策略

     1、谨慎使用large heap

          由于软硬件的不同,Android设备的heap阀值也不同。可以在manifest中来使用largeheap=true声明一个更大的空间。但是要谨慎使用,因为额外的空间会影响到系统整体的用户体验,使GC的运行时间更长,切换任务时性能打折扣。

     2、要关注内存的开支情况

          在编码过程中一些看些无关痛痒的写法,结果会导致很大一部分的内存开支:

          (1)使用枚举通常会比使用静态常量要消耗两倍以上的内存,故在Android开发中尽可能地不使用枚举

          (2)任何一个Java类,包括内部类、匿名类,都要占用大概500字节的内存空间

          (3)任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也会影响内存

          (4)在使用HashMap时,即使设置的是基本数据类型的key,比如说int,但是也会按照对象的大小类分配内存,大概是32字节,而不是4字节。因此最好是使用优化过的数据集合,比如SparseArray、SparseLongArray、SparseBooleanArray、ArrayMap等。

     3、节制的使用Service

           启动一个service服务时,系统会倾向将这个Service所依赖的进程进行保留,这样就会导致该进程会消耗更多的内存,并且系统在LRU cache中缓存的进程数量也会减少,导致切换应用程序时耗费更多性能。严重时或导致系统崩溃,因为系统在内存资源吃紧的时候可能已无法维护所有正在运行的Service所依赖的进程了。所以为了控制Service的生命周期,Android官方推荐使用IntentService,这种service的最大特点就是当后台任务执行结束后会自动停止,从而极大程度的避免了Service内存泄漏。

     4、谨慎使用static对象

          因为static的生命周期过长,和应用的进程保持一致,使用不当很可能导致内存泄漏

     5、优化布局

          越扁平化的视图布局,占用的内存就越少,效率越高。当系统提供的View无法实现足够扁平的时候可以考虑使用自定义的View来达到目的。

     6、谨慎使用“抽象”编程

          使用抽象编程可以提升代码的扩展性和可维护性,但是也会随之带来额外的内存开支,因为抽象编程需要编写额外的代码,尽管这些代码可能执行不到,但也会映射到内存中,不仅占用内存,在执行效率方面也会有所降低。

     7、谨慎使用依赖注入框架

          使用框架注入代码,可以简化代码的编写。但是这些框架为了要搜寻代码中的注解,通常要经历较长时间的初始化过程,并且还会将一些用不到的对象也一并加载到内存当中。这些用不到对象会一直占着内存空间,可能要经过很久才会释放。

     8、 当界面不可见时释放内存

           在程序界面不可见时,应将所有和界面相关的资源进行释放。在这种场景下释放资源可以让系统缓存后台进程的能力显著增加,可以给用户更好的体验。判断程序界面不可见,采用重写Activity中的onTrimMemory()方法,然后在这个方法中监听TRIM_MEMORY_UI_HIDDEN这个级别,一旦触发了之后就说明用户已离开我们的程序,此时就可以进行资源释放。注意该方法的回调只要当我们程序中的所有UI组件全部不可见的时候才会触发,和onStop()有很大区别。

     9、慎重使用多进程

           使用多进程可以把应用中的部分组件运行在单独的进程中,可以扩大内存的占用范围。但要谨慎使用多进程,因为使用多进程会使得代码逻辑更加复杂,不易掌控,一旦使用不当,不仅不会节省内存还会导致内存显著增加。当应用需要一个常驻后台的任务并且这个任务并不轻量,可以考虑使用这个技术(典型例子:音乐播放器)。

    10、 资源文件选择合适的文件夹存放

            不同dpi文件夹下的图片在不同的设备上会经过scale处理。例如在hdpi的目录下放置了一张100*100的图片,根据换算关系,xxhdpi的手机去引用这张图片就会被拉伸到200*200,这就导致内存增高。因此,对于不希望被拉伸的图片,要放到assets或者nodpi文件夹下

    11、使用try catch

           对于可能发生OOM的代码,加入catch机制,在catch中尝试一下降级的内存分配操作

    12、谨慎使用第三方库

          在使用第三方库之前,要了解清楚是否是针对移动开发环境编写的,该库具体有什么功能,和现有功能有没有冲突......要将这些问题了解清楚,不然内存的增加几率就会大大增加。

 

以上是针对OOM和内存管理方面写的一些经验总结,如有不当之处,欢迎指正!仅供参考学习,谢谢!

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值