Android OOM 问题的总结

问题: 安卓系统常常遇到OOM问题,如何优化和应对?java

    Dalvik 主要管理的内存有 Java heap 和 native heap 两大块,而对于一个安卓应用来讲,因为手机设备的限制,通常应用使用的RAM不能超过某个设定值,若是你想要分配超过大于该分配值的内存的话,就会报Out Of Memory 错误。不一样产商默认值不太同样,通常常见的有16M,24M,32M,48M也就是说app的 Java heap + native heap < 默认值。只因此不等因而由于除了这2种咱们主要关心的内存外,还有一些小块的内存块用作其余用途,如用于垃圾回收的markstack,markbits以及livebits。android

致使OOM 有如下几种状况:缓存

1 应用中须要加载大对象,例如Bitmapapp

一张在pc机上用的1024*768图片,若是直接用在手机屏幕这种小屏幕上,不只没有提升显示质量,还容易使内存吃紧。假设照片是用ARGB_8888格式,那么一张1024&times;768的图片须要占用3M的内存, 4-5张就OOM了。bitmap分辨率越高,所占用的内存就越大,这个是以2为指数级增加的。优化

     

解决方案:当咱们须要显示大的bitmap对象或者较多的bitmap的时候,就须要进行压缩来防止OOM问题。咱们能够经过设置BitmapFactory.Optiions的inJustDecodeBounds属性为true,这样的话不会加载图片到内存中,可是会将图片的width和height属性读取出来,咱们能够利用这个属性来对bitmap进行压缩。Options.inSampleSize 能够设置压缩比。spa


ps: 在android 2.3和之前的版本,bitmap对象的像素数据都是分配在native heap中的,因此咱们在调试过程当中这部份内存是在java heap中看不到的,不过在android 3.0以后,bitmap对象就直接分配在java heap上了,这样便于调试和管理。所以在3.0以后咱们能够复用bitmap的内存,而没必要回收它,不过新的bitmap对象要大小和原来的同样,到了android 4.4以后,就只要高宽不超过原来的就好了线程

   此外,java heap 的大小是动态变化的,它像是一个容器,只有水快满了,在容许的范围内,它会扩充它的容量。当水常处于较少的状况下它会缩小其容量. 上面说过android 2.3以前bitmap对象都是在native heap中分配的,若是咱们在生成一个bitmap对象以前,在java heap 分配了一块较大的内存,即便以后这段内存被回收了,可是咱们的java heap大小不可能一会儿就随着这块内存的回收而缩小,它须要一个过程。而咱们又要在native上分配一个bitmap对象的内存,若是这时候 默认值-java heap(未缩小) = native heap的值不足以分配给这个bitmap对象,那么就会报OOM错误。调试


2 持有无用的对象使其没法被gc,致使Memory Leak . 也就是咱们说的内存泄漏。致使内存泄漏我了解的有如下几个方面。code


2.1 静态变量致使的Memory leak对象

静态变量的生命周期和类是息息相关的,它们分配在方法区上,垃圾回收通常不会回收这一块的内存。因此咱们在代码中用到静态对象,在不用的时候若是不赋null值,消除对象的引用的话,那么这些对象是很难被垃圾回收的,若是这些对象一多或者比较大的话,程序出现OOM的几率就比较大了。由于静态变量而出现内存泄漏是很常见的。

2.2 不合理使用Context 致使的Memory leak

  android 中不少地方都要用到context,连基本的Activty 和 Service都是从Context派生出来的,咱们利用Context主要用来加载资源或者初始化组件,在Activity中有些地方须要用到Context的时候,咱们常常会把context给传递过去了,将context传递出去就有可能延长了context的生命周期,最终致使了内存泄漏。例如 咱们将activty context对象传递给一个后台线程去执行某些操做,若是在这个过程当中由于屏幕旋转而致使activity重建,那么原先的activity对象不会被回收,由于它还被后台线程引用着,若是这个activity消耗了比较多的内存,那么新建activity或者后续操做可能由于旧的activity没有被回收而致使内存泄漏。因此,遇到须要用到context的时候,咱们要合理选择不一样的context,对于android应用来讲还有一个单例的Application Context对象,该对象生命周期和应用的生命周期是绑定的。选择context应该考虑到它的生命周期,若是使用该context的组件的生命周期超过该context对象,那么咱们就要考虑是否能够用application context。若是真的须要用到该context对象,能够考虑用弱引用来WeakReference来避免内存泄漏。

2.3 非静态内部类致使的Memory leak

非静态的内部类会持有外部类的一个引用,因此和前面context说到的同样,若是该内部类生命周期超过外部类的生命周期,就可能引发内存泄露了,如AsyncTask和Handler。由于在Activity中咱们可能会用到匿名内部类,因此要当心管理其生命周期。 若是明确生命周期较外部类长的话,那么应该使用静态内部类。

2.4 Drawable对象的回调隐含的Memory leak

当咱们为某一个view设置背景的时候,view会在drawable对象上注册一个回调,因此drawable对象就拥有了该view的引用了,进而对整个context都有了间接的引用了,若是该drawable对象没有管理好,例如设置为静态,那么就会致使Memory leak


在手机版QQ中,对于图片的处理方法是先加载用户发送的图片的小图,只有用户点击了才去加载大图,这样即节省了流量也能防止bitmap过大形成OOM 。另外,对于离开屏幕内容的图片,也要及时的回收,否则聊天记录一多也就OOM了。 对于回收的图片,咱们能够采用软引用来作缓存, 这样在内存不吃紧的状况下就能提升交互性,由于在Java中软引用在内存吃紧的时候才会被垃圾回收,比较适合用作cache.另外在Android 3.1版本起,官方还提供了LruCache来进行cache处理。对于不用的Bitmap对象,咱们要及时回收,不然会形成Memory leak ,因此当咱们肯定Bitmap对象不用的时候要及时调用Bitmap.recycle()方法来使它尽早被GC

相关文章
相关标签/搜索