Android内存泄漏------常见场景

内存管理和引用类型

内存泄漏的检测流程、捕捉以及分析

1、单例造成的内存泄露

单例模式是非常常用的设计模式,使用单例模式的类,只会产生一个对象,这个对象看起来像是一直占用着内存,但这并不意味着就是浪费了内存,内存本来就是拿来装东西的,只要这个对象一直都被高效的利用就不能叫做泄露。

实质是静态变量引用Activity,在getInstance(Context context)方法中传入的参数context如果是某个Activity,但是Activity的生命周期较短,而单例作为Static对象是全生命周期的,这样当Activity结束后,单例对象仍然持有Activity的引用,GC机制无法判定为垃圾进行回收,此时发生内存泄露。

public class SingleInstanceTest {

    private static SingleInstanceTest sInstance;
    private Context mContext;

    private SingleInstanceTest(Context context){
        //不能直接使用Activity或者短周期的Context
        this.mContext = context.getApplicationContext();
    }

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

二、非静态内部类导致内存泄露

非静态内部类自动获得外部类的强引用,而且它的生命周期甚至比外部类更长,这便埋下了内存泄露的隐患。如果一个 Activity 的非静态内部类的生命周期比 Activity 更长,那么 Activity 的内存便无法被回收,也就是发生了内存泄露,而且还有可能发生难以预防的空指针问题。解决办法:将内部类声明为静态内部类,使其无法于外部类建立联系。

三、匿名内部类导致内存泄露

比方说Thread、Handler等
1、new 出一个匿名的 Thread,进行耗时的操作,如果 MainActivity 被销毁而 Thread 中的耗时操作没有结束的话,便会产生内存泄露;
解决方法:继承 Thread  实现静态内部类
2、new 出一个匿名的 Handler,这时如果 MainActivity 被销毁,而 Handler 里面的消息还没发送完毕的话,Activity 的内存也不会被回收;
解决方法:
A:继承 Handler 实现静态内部类,以及在 Activity 的 onDestroy() 方法中,移除所有的消息 mHandler.removeCallbacksAndMessages(null);
B:通过内部静态类和弱引用的方法来及时释放Activity,需要注意的是在Activity退出时记得清除掉消息队列中的消息

public class MainActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                。。。。。。
            }
        }).start();


        Message message = Message.obtain();
        mHandler.sendMessageDelayed(message, 10000);
    }

四、Handler导致内存泄露

其实还是匿名内部类的问题,都是错误的创建Handler,导致非静态对象引用了Activity

public class MainActivity extends AppCompatActivity {
    private MyHandler mHandler = new MyHandler(this);
    private TextView mTextView ;
    private static class MyHandler extends Handler {
        private WeakReference<Context> reference;
        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = (MainActivity) reference.get();
            if(activity != null){
                activity.mTextView.setText("");
            }
        }
    }

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

五、资源未关闭导致内存泄露

对于使用了ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
还有其他:
手动注册广播时,退出时忘记 unregisterReceiver()
Service 执行完后忘记 stopSelf()
EventBus 等观察者模式的框架忘记手动解除注册

六、集合中对象没清理造成的内存泄漏

Android主要的集合类
1、List 主要有ArrayList、LinkedList、Vector和Stack 
2、Set 主要有HashSet 和 TreeSet 
3、Map 主要有 HashMap 和 TreeMap 
参考:https://blog.csdn.net/dbpggg/article/details/80825294
https://blog.csdn.net/gundumw100/article/details/69977700
https://www.jianshu.com/p/37bb043aef73

编程过程中,我们常常会把一些对象加入到集合中。在我们不再需要该对象时,如果没有及时把它从集合中清理掉,就会导致这个集合占用的内存越来越大。
同时如果这个集合是静态的话,那情况就更严重了。
解决办法:集合中不再使用的对象应及时释放掉。应该在Activity的onDestroy()方法中,及时清理set里的元素,避免无用对象继续存在强引用。

public class MainActivity extends AppCompatActivity {
    static List<Object> objectList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i = 0; i < 10; i++) {
            Object obj = new Object();
            objectList.add(obj);
            obj = null;
        }
    }

    public void onDestory(){
        objectList.clear();
        objectList = null;
    }
}

七、ListView的内存泄漏问题

构造Adapter时,没有使用缓存的 convertView,导致内存泄露。
来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。

public View getView(int position, View convertView, ViewGroup parent) {
  View view = null;
  if (convertView != null) {
  view = convertView;
  ...
  } else {
  view = new Xxx(...);
  ...
  }
  return view;
}

八、static关键字所导致的内存泄漏

public class SecondActivity extends Activity{
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            SecondActivity.this.finish();
            this.removeMessages(0);
        }
    };
 
    private static Hehe hehe;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        haha = new Haha();
        mHandler.sendEmptyMessageDelayed(0,5000);
    }
 
    class Hehe{
 
    }
}

其实还是非静态内部类的静态引用。然后在5秒之后我们要finish掉这个activity,会造成什么问题呢?我们知道,内部类和外部类之间是相互持有引用的,SecondActivity实例持有了hehe的引用,但这里hehe是用static修饰的,上面说了,虚拟机不会回收hehe这个对象,从而导致SecondActivity实例也得不到回收,造成内存溢出
解决办法:只要在Activity的onDestroy方法里把haha设为null就行啦

 

九、不当的使用内存,虽然不能导致泄漏,但是可能导致OOM

猜你喜欢

转载自blog.csdn.net/pangjl1982/article/details/85220896
今日推荐