Android内存泄漏

概念

  • 内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间。
    对象被更长生命周期的对象持有引用,导致无法被GC回收。【无用对象无法被回收】
  • 内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用

影响

  • 无用对象无法及时释放,占用内存
  • 严重时造成内存溢出

常见情况分析

Android多发生于Activity、Fragment等对象在生命周期结束后不能及时释放

1. 单例模式

常见于单例模式的工具类需要持有Context对象,此情况下如果直接传Activity对象来进行初始化,会造成单例一直持有此Activity引用,无法释放。

public class Singleton {

    private static Singleton sInstance;
    private Context mContext;

    private Singleton (Context context){
        this.mContext = context;
    }

    public static Singleton newInstance(Context context){
        if(sInstance == null){
            sInstance = new Singleton (context);
        }
        return sInstance;
    }
}

解决方案:

  • 如果单例模式必须持有context对象,需要使用与应用生命周期一样的Application对象
  • 对于工具类必须使用到activity等短生命周期对象的情况,可以选择仅在操作方法中作为参数传入,这样在方法执行完成后会可以正常释放
public class Singleton {
private static Singleton sInstance;
 ......
  public void func(Activity activity){
      //do something
  }
}

2. 非静态内部类

由于非静态内部类、匿名内部类等会持有外部类的引用,尤其需要注意使用Thread、Runnable、AsyncTask等新开线程的类时,常常习惯于直接使用匿名内部类,很容易造成外部类内存泄漏。

class A{
  private mB = new B();
  //内部类B持有外部类A的引用
  class B{
  //....
  }
}

解决方案:
使用静态内部类或者外部类


3. Handler

Handler 造成内存泄漏的原因也是持有内部类持有外部类的引用。

public class TestActivity extends Activity {
  int mCount =0;

  private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
          //收到消息计数+1
            super.handleMessage(msg);
            mCount++;
        }
    };
  //使用Handler 发送消息
  public void send(){
        Message message = Message.obtain();
        mHandler.sendMessageDelayed(message, 1000);
  }
}

mHandler是匿名内部类而持有外部TestActivity引用。
解决方案:

  • 如果消息是延时发送(sendMessageDelay),Handler 里面的消息还没发送完毕的话,Activity 的内存也不会被回收,需要移除消息。

  • 改用静态内部类或外部类。同时因为使用Handler一般在收到消息后需要与Activity交互,此时可以使用Activity的弱引用。

public class TestActivity extends Activity {
    private Handler mHandler = new MyHandler(this);

    //移除消息
    @Override
    onDestroy() {
        mHandler.removeCallbacksAndMessages(null)
    }

    //静态内部类
    static class MyHandler extends Handler {
        //弱引用持有Activity
        private WeakReference mReference;

        public MyHandler(Activity activity) {
            mReference = new WeakReference(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            final Activity activity = mWeakReference.get();
        }
    }
}

4. 集合持有子元素

集合会持有子元素引用,如果子元素属于无用对象,应该及时从集合清除


5. 资源没有及时关闭

在使用完File,Cursor,Stream,Bitmap等资源时,要及时关闭释放内存。


6. Rxjava内存泄漏

通常使用RxJava做网络加载时线程调度,很可能由于子线程未执行 完毕导致内存泄漏。
解决方案:

  • 使用CompositeDisposable管理
class Presenter  {
    private CompositeDisposable mDispose;
    //订阅事件天机到CompositeDisposable
    private void addDispose(Disposable disposable) {
        if(mDispose== null){
            mDispose =new  CompositeDisposable();
        }
        mDispose.add(disposable);
    }
    
    //生命周期结束前调用dispose方法
    @Override
    public void unbind(){
        super.unbind();
        mDispose.dispose();
        mDispose = null;
    }
    
    //测试
    public void funcTest() {
        addDispose(Observable.just(1)
                .subscribeWith(observer..));
    }
    
}

7. MVP模式内存泄漏

由于P层V层相互持有引用,因此在V层(通常是Activity、Fragment等)生命周期结束前(如Activity.onDestroy时)需要解除绑定


总结

内存泄漏重点是无用的对象被生命周期较长的对象引用导致GC时无法释放。
解决的办法主要是从

    1. 短生命周期周期对象尽量避免持有长生命周期对象引用
    1. 必须持有的,及时移除对长生命周期对象的引用
    1. 利用弱引用的特性
    1. 及时关闭Cursor、File等资源

你可能感兴趣的:(Android内存泄漏)