内存泄漏场景和内存泄漏优化

优化内存的意义

  1. 减少oom,提高应用的稳定性
  2. 减少卡顿,提高应用的流畅度
  3. 减少内存的占用,提高应用后台运行时的存活率
  4. 减少异常发生,减少代码逻辑隐患。

常见内存泄漏场景

1.资源对象没关闭造成的内存泄露,try catch finally中将资源回收放到finally语句可以有效避免OOM。资源性对象比如:

1-1,Cursor
1-2,File文件,未关闭InputStream/OutputStream
资源性对象往往使用了一些缓冲,在不使用的时候应该及时关闭,以便它们的缓冲数据能及时回收

2.注册对象未注销

1-1,调用registerReceiver后未调用unregisterReceiver()
如果时间注册后未注销,会导致观察者列表中维持着对象的引用,阻止垃圾回收,一般发生在注册广播接收器、注册观察者等。

3.作用域不一样,导致对象不能被垃圾回收器回收,比如:

2-1,非静态内部类会隐式地持有外部类的引用,
2-2,Context泄露
概括一下,避免Context相关的内存泄露,记住以下事情:
1、 不要保留对Context-Activity长时间的引用(对Activity的引用的时候,必须确保拥有和Activity一样的生命周期)
2、尝试使用Context-Application来替代Context-Activity
比如 单例模式导致的内存泄漏
单例模式的生命周期和Application保持一致,如果单例对象持有Activivty 的引用,因此,此Activity 对象无法及时释放

3、如果你不想控制内部类的生命周期,应避免在Activity中使用非静态的内部类,而应该使用静态的内部类,因为静态内部类不会持有外部类的引用,所以不会导致外部类实例出现内存泄露。并在其中创建一个对Activity的弱引用。
这种情况的解决办法是使用一个静态的内部类,其中拥有对外部类的WeakReference。
比如Handler引起的内存泄漏

private Handler mHandler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
            // your code  
        }  
    };

当使用内部类或匿名内部类的方式创建Handler时,Handler对象会隐式地持有一个外部类对象的引用(这里的外部类是Activity)。一般在一个耗时任务中会开启一个子线程,如网络请求或文件读写操作,我们会使用到Handler对象。但是,如果在任务未执行完时,Activity被关闭了,Activity已不再使用,此时由GC来回收掉Activity对象。由于子线程未执行完毕,子线程持有Handler的引用,而Handler又持有Activity的引用,这样直接导致Activity对象无法被GC回收,即出现内存泄漏。

解决方法主要在于两点:
①.将Handler声明为静态内部类。因为静态内部类不会持有外部类的引用,所以不会导致外部类实例出现内存泄露。
②.在Handler中添加对外部Activity的弱引用。由于Handler被声明为静态内部类,不再持有外部类对象的引用,导致无法在handleMessage()中操作Activity中的对象,所以需要在Handler中增加一个对Activity的弱引用。
③在Activity的Destroy或者Stop时,应该移除消息队列里的消息,避免Looper线程的消息队列中有待处理的消息需要处理。

4.容器中的对象没有清理造成的内存泄漏

通常把一些对象的引用加入集合,在不需要改对象的时候,如果没有把它的引用从集合中清理掉,这个集合就会越来越大。如果集合是static,情况就会越来越严重。

5.内存压力过大

5-1,图片资源加载过多,超过内存使用空间,例如Bitmap 的使用,android系统给图片分配的内存只有8M, 图片尽量使用软引用, 较大图片可通过BitmapFactory缩放后再使用,并及时recycle
5-2,重复创建view,listview应该使用convertview和viewholder

4.static关键字
开发中使用关键字static可以将成员变量和方法变成类变量和类方法,这样会大大延长变量的生命周期,如果我们过多的使用static来保存占用资源过多的对象的引用就会造成内存溢出,比如用static修饰一个上下文的对象的话.
第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。
第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

5.属性动画导致的内存泄漏
属性动画中有一类无限循环的动画,如果在Activity中播放此类动画而且没有去onDestory 中去停止动画,那么动画会一直播下去,尽管已经无法在界面上看到显示效果了,并且这个时候Activity的View 会被动画持有,而View 又持有Activity,最终Activity 无法释放。解决办法:是在Activity的OnDestory 中调用animation.cancel()来停止动画

如何避免内存泄露:

1.内存复用

①有效利用系统自带的资源
②视图复用,出现大量重复子组件,而子组件是大量重复的,可以使用ViewHolder实现ConvertView复用,这基本是所有容器控件的处理方式
③对象重复并且频繁调用可以考虑对象池,在程序中创建对象池,然后实现复用逻辑,对相同的类型数据使用同一块内存空间,也可以利用系统框架既有的具有复用性的组件减少对象的重复创建,从而减少内存的分配与回收

2.对象引用

对于引用生命周期不一样的对象,可以用软引用或弱引用SoftReferner WeakReferner

3.对于资源对象

使用finally 强制关闭

4.使用缓存技术

比如LruCache、DiskLruCache

开源图片组件对比

Picasso

使用复杂的图片压缩转换来尽可能地减少内存消耗
自带内存和硬盘二级缓存功能

Fresco

  1. 内存管理
    Android中的Bitmap会占用大量的内存,很容易引发频繁的GC。在Android5.0以下,GC会显著的引发界面卡顿。而在Android5.0以下系统,Fresco将内存放在一个特别的内存区域。在图片不显示的时候,占用的内存会自动释放。这使得应用更加流畅,同时减少因图片占用内存而引发的OOM。
  2. 渐进式呈现
    是指先呈现大致的图片轮廓,然后随着图片的下载继续,呈现逐渐清晰的图片,这在移动设备,特别是在网络环境较差的情况下,可以带来更好的用户体验
  3. 更多的图片格式
    支持Gif和WebP格式
  4. 图片加载策略丰富
    使开发者可以再多方面控制图片的加载,为同一个图片指定不同的远程路径,比如先显示存在本地缓存中的图片,等高清图下载完之后再显示高清图。
    虽然功能强大,但是SDK安装包很大,如果应用本身需要显示的图片不多或者场景很少,建议使用更轻量的组件

猜你喜欢

转载自blog.csdn.net/luyuqin0115/article/details/81630303