1、android中的内存泄露

对于android中一些无用的对象,比如activity,view长期持有这些对象,导致无法被GC回收利用,再次使用的,重新申请内存。长久下去,应用程序无法分配新内存,导致OOM。
大部分内存泄露,不会直接导致程序崩溃,但长期下去,应用程序无法负担。一反面使用app耗用大量内存,卡顿,别一方面。最终还是可能导致OOM导常。

square比较好用的内存检测工具
https://github.com/square/leakcanary

2、android中常见的内存泄露地方

1、不合理的使用static静态变量。
2、不合理的使用单例。
3、handler引起的内存泄露。
4、查询数据库,对文件读写。未关闭资源(cursor没有关闭)。
5、不合理的监听(该对象已经移除,还对该对象进行监听)比如,内容观察者没有在适合的地方移除。
6、内部类的不适当使用。

这几个地方,在android开发的过程中,经常遇到。因此在这几个地方,要特别注意,防止发生内存泄露。

3、慎用static关键字

使用静态static静态变量潜在性问题:

1.占用内存,并且内存一般不会释放;

2.在系统不够内存情况下会自动回收静态内存,这样就会引起访问全局静态错误。

3.不能将activity作为static静态对象,这样使activity的所有组件对象都存入全局内存中,并且不会被回收;

1、静态集合引发内存泄露

在某些情况下可能需要静态集合做些缓存数据,但是。我们可能在每次使用的时候,给静态集合中添加了数据,但从没有移除过数据,导致集合中数据越来越多。因此使用静态集合的时候,一定记得在适合的情况下移除集合中的数据,防止内存泄露。

2、单例模式引起内存泄露

public class Singleton {
    private static Singleton singleton;
    private Context context;

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

    }

    /**
     * 单例引起的内存泄露;
     * @param context
     * @return
     */
    public synchronized static Singleton getSingleton(Context context) {
        if (singleton == null) {
            singleton = new Singleton(context);
        }
        return singleton;
    }

}

APP内存泄露 Android 内存泄漏 android_APP内存泄露 Android


由于单例拥持有了当前Activity对象,所以当前Activity没有办法回收,导致MainActivity内存泄露。

解决办法:把context换成application的context;

public class Singleton {
    private static Singleton singleton;
    private Context context;

    /**
     * 把生命周期更长的application的上下文传入
     *
     * @param context
     */
    private Singleton(Context context) {
        this.context = context.getApplicationContext();

    }

    /**
     * 单例引起的内存泄露;
     *
     * @param context
     * @return
     */
    public synchronized static Singleton getSingleton(Context context) {
        if (singleton == null) {
            singleton = new Singleton(context);
        }
        return singleton;
    }

}

3、Handler引起的内存泄露

APP内存泄露 Android 内存泄漏 android_APP内存泄露 Android_02


android handler机制用于我们从子线程返回的数据后,刷新ui。但在一些情况下,后台仍然在执行耗时操作(网络请求,数据库查询等)时,当前activity已经销毁,被回收,而我们还在等待返回数据后ui刷新。当前的handler持有对activity的引用,因此,activity也没有办法销毁。

public class SecondActivity extends AppCompatActivity {

    private TextView textView;
    private MyHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        textView = (TextView) findViewById(R.id.text);
        mHandler=new MyHandler();
        Message message = new Message();
        message.arg1 = 1;
        mHandler.sendMessageDelayed(message, 5000);
        finish();

    }
    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    textView.setText("123");
                    break;
            }
        }
    }
}

解决办法:

public class SecondActivity extends AppCompatActivity {

    private TextView textView;
    private MyHandler mHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        textView = (TextView) findViewById(R.id.text);
        mHandler=new MyHandler(this);
        Message message = new Message();
        message.arg1 = 1;
        mHandler.sendMessageDelayed(message, 5000);
        finish();

    }

    public void notifyView() {
        textView.setText("12");
    }

    private static class MyHandler extends Handler {
        private final WeakReference<SecondActivity> mActivity;

        public MyHandler(SecondActivity activity) {
            mActivity = new WeakReference<SecondActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            System.out.println(msg);
            if (mActivity.get() == null) {
                return;
            }
            mActivity.get().notifyView();
        }
    }
}

在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。

android中的很多内存泄露都是由于在Activity中使用了非静态内部类导致的,我们在使用非静态内部类一定要格外注意,如果该静态内部类的实例对象的生命周期大于外部对象,那么就有可能导致内存泄露,推荐使用上面介绍的静态类和弱引用的方法解决这种问题。

4、LeakCanary内存泄露工具分析

leakcanary的确很大程度帮我们找出内存泄露的地方,方便的们及时排查内存泄露的代码。
leakcanary其实相当于把所有activity(Application)的生命周期回调放在一个集合中,这个集合存放activity回调,leakcanary对activity的onDestory方法进行检测。
Application的ActivityLifecycleCallbacks回调接口中的onDestory()回调实现对项目中的activity进行对象监听,然后交给RefWatcher监听类进行进一步处理。

LeakCanay的入口是在application的onCreate()方法中声明的,其实用的就是Application的ActivityLifecycleCallbacks回调接口监听所有activity的onDestory()的,在这个方法进行RefWatcher.watch对这个对象进行监控。

具体是这样做的,封装成带key的弱引用对象,然后GC看弱引用对象有没有回收,没有回收的话就怀疑是泄漏了,需要二次确认。然后生成HPROF文件,分析这个快照文件有没有存在带这个key值的泄漏对象,如果没有,那么没有泄漏,否则找出最短路径,打印给我们,我们就能够找到这个泄漏对象了。