Android 常见的内存泄露

当一个对象不再需要时,本应该被回收,而被另一个正在使用的对象(可能是静态对象,也有可能是匿名内部类对象,等等)引用导致不能正常被回收,就会一直存活在堆中,造成内存泄露。内存泄露会使程序造成程序OOM(Out Of Memory),导致应用Crash。

单例造成的内存泄漏

由于单例的静态特性,使得单例对象和应用的生命周期一样长。所以被单例对象引用的对象,如果不主动置为null释放对象,那么被引用的对象便和单例对象“共生死”,即使被引用的对象不需要了,也不能被回收,从而造成内存泄露。
如例子:

public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

这样写本来没错,如果你这么用的话:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        AppManger appManager = AppManager.getInstance(this);
    }
}

那MainActivity就别想被回收了,给我老实待在堆内存中吧!!原因是被单例AppManager所引用。改善的方法:

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        AppManger appManager = AppManager.getInstance(getApplicationContext());
    }
}

这样使用的话,MainActivity就不会被引用,如果没有其他地方引用MainActivity的话,等finish了,就可以等着被回收了。

非静态内部类造成的内存泄露

非静态内部类(包括匿名内部类)会持有外部类对象的强引用(如 Activity),所以很容易导致内存泄露,比如 Handler, Runnable 。

Handler内存泄露
public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // TODO Something
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
        finish();
    }
    private void loadData() {
        Message message = Message.obtain();
        mHandler.sendMessageDelayed(message, 100000);
    }
}

因为mHandler是MainActivity的内部类,所以隐式持有MainActivity这个实例。由于Handler还有没有处理的Message,而Message持有mHandler实例,此时即使MainActivity关掉了,内存资源也无法及时被回收,造成内存泄露。解决办法:

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 onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView)findViewById(R.id.textview);
        loadData();
        finish();
    }
    private void loadData() {
        //...request
        Message message = Message.obtain();
        mHandler.sendMessageDelayed(message, 100000);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}
  1. 把非静态内部类声明为静态内部类(其实就相当于单独的一个类,写在某个类里面而已),这样的话mHandler就不再直接持有MainActivity实例了。
  2. 因为要用到MainActivity实例,使用弱引用方式引用MainActivity实例,这样即使mHandler还有没处理的Message,等MainActivity关掉了,MainActivity资源也可以正常被回收。
  3. 虽然步骤1和2避免了Activity泄漏,不过Looper线程的消息队列中还是可能会有待处理的消息,所以我们在Activity的Destroy时或者Stop时应该调用mHandler的removeCallbacksAndMessages或者removeCallbacks移除消息队列中的消息。
Runnable内存泄露
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                SystemClock.sleep(10000);
            }
        }).start();
        finish();
    }
}

和Handler一样,因为new出来的Thread匿名内部类也隐式持有MainActivity实例,所以会造成内存泄露,解决的办法如解决Handler内存泄露的原理一样,不再赘述。

资源未关闭造成的内存泄露

  • BroadcastReceiver,ContentObserver 之类的没有解除注册

  • Cursor,Stream 之类的没有 close

  • 无限循环的动画在 Activity 退出前没有停止

  • 一些其他的该 release 的没有 release,该 recycle 的没有 recycle… 等等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值