Android内存泄漏及解决方法

背景

在Android开发中内存泄漏是一个相对来说比较常见的问题,这个问题也相当严重,但是有好多朋友还不知道怎么解决和查看内存泄漏问题,这里就写一篇文章来给大家介绍一些常见的内存泄漏问题以及解决方法,如果想看内存泄漏检测的可以看这里 Android内存泄漏检测

常见内存泄漏

1. 静态引用

比如以下代码,定义了sInstance来传递和使用,会导致MainActivity无法被销毁,这是一种比较低级的错误,一般我们不建议这么使用,如果一定要使用,就需要在最后将sInstance置空。

public class MainActivity extends Activity {
    private static Activity sInstance;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sInstance = this;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        sInstance = null;
    }
}

2. 内部类持有外部类的引用,内部类被静态引用,等同于外部类被静态引用

在JAVA中内部类的定义与使用一般为成员内部类与匿名内部类,他们的对象都会隐式持有外部类对象的引用,影响外部类对象的回收。
GC只会回收没有被引用或者根集不可到达的对象(取决于GC[算法]),内部类在生命周期内始终持有外部类的对象的引用,造成外部类的对象始终不满足GC的回收条件,反映在内存上就是内存泄露。(如,[Android]中Activity的内存泄露)
解决方案为
1.将内部类定义为static
2.用static的变量引用匿名内部类的实例或将匿名内部类的实例化操作放到外部类的静态方法中

public class MainActivity extends Activity {
    private static TestClass sTestClass;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    public class TestClass{

    }
}

3. AsyncTask

下面的代码我们在退出的时候mAsyncTask是无法被销毁的,会导致Activity无法被销毁:

private AsyncTask mAsyncTask;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAsyncTask = new AsyncTask() {
            @Override
            protected Object doInBackground(Object[] objects) {
                while (true);
            }
        };
        mAsyncTask.execute();
    }

解决办法如下代码:在Activity销毁的时候取消正在运行的AsyncTask

public class MainActivity extends Activity {
    private AsyncTask mAsyncTask;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAsyncTask = new AsyncTask() {
            @Override
            protected Object doInBackground(Object[] objects) {
                while (true){
                    if(isCancelled()){
                        break;
                    }
                }
                return null;
            }
        };
        mAsyncTask.execute();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(null != mAsyncTask && !mAsyncTask.isCancelled()){
            mAsyncTask.cancel(true);
        }
        mAsyncTask = null;
    }
}

4. Handle造成的内存泄漏

4.1 用过内部类创建的Handler对象,持有Activity的引用。当执行postDelayed()方法时,会把Handler装入一个Message,并把Message推到MessageQueue中,MessageQueue在一个Looper线程中不断轮询处理消息。
因为延迟的时间足够长,当Activity退出时,消息队列还未处理Message,从而引发OOM

public class MainActivity extends Activity {
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendEmptyMessageDelayed(0,1000);
    }
}

那么对于上面产生的内存泄漏我们要怎么处理呢?采用弱引用的形式:

public class MainActivity extends Activity {
    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler(new WeakReference<Activity>(this));
        mHandler.sendEmptyMessageDelayed(0,1000);

    }

    public static class MyHandler extends Handler{
        private WeakReference<Activity> mActivity;
        public MyHandler(WeakReference<Activity> activityWeakReference){
            mActivity = activityWeakReference;
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(null != mActivity){
                Activity activity = mActivity.get();
                if(null != activity && !activity.isFinishing()){
                    //TODO do something...
                }
            }
        }
    }
}

4.2 内部调用Handler,解决办法,在Activity销毁的时候将Handler里面的信息清除。

public class MainActivity extends Activity {
    private Handler mHandler = new Handler();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {

            }
        },1000);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}

5. Thread引起的内存泄漏

下面的方法会产生内存泄漏,相信大概都知道,那么我们应该怎么去解决呢?

public class MainActivity extends Activity {
    private Thread mThread;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mThread = new Thread(){
            @Override
            public void run() {
                super.run();
                while (true){

                }
            }
        };
        mThread.start();
    }
}

通过前面几种内存泄漏的解决方法,我相信大家应该也能猜出个大概,无非就是在Activity销毁的时候去暂停Thread那么我们应该怎么做呢?

@Override
    protected void onDestroy() {
        super.onDestroy();
        if(null != mThread){
            mThread.interrupt();
        }
    }

interrupt()和Thread.interrupt()的区别,其中interrupt()是作用于调用线程的,比如我们上面调用的,他是作用于mThread这个线程的,如果我们在上面使用Thread.interrupt()那么就是作用于主线程的。

6. WebView造成的内存泄漏

WebView在解析网页时,会申请Native堆内存,保存页面元素(图片、history等)

7. 其他原因

BraodcastReceiver
ContentObserver
File
Cursor
Stream
Bitmap
Timer

总结

上面是我在平时开发中遇到的一些关于内存泄漏的地方,大家有什么其他的内存泄漏或者对于以上有更好的解决方法,欢迎留言。楼主还写了一篇关于 Android内存泄漏检测的有兴趣的同学也可以看一下。

    原文作者:Only凹凸曼
    原文地址: https://www.jianshu.com/p/04fe91586d4c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞