关于匿名内部类,非静态内部类会造成内存泄露的隐患。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhiyuanIT/article/details/48089251

注意是内存泄露,不是内存溢出。啊吐舌头

首先先看一下下面这样一段代码

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                SystemClock.sleep(1000);
            }
        }
    }).start();
}

Thread 是一个匿名内部类,,当我们finish的时候,该activity实例不会真正销毁,GC机制也不会进行该实例的垃圾回收,因为匿名内部类和非静态内部类持有外部类的强引用,也就是说Thread持有外部activity的强引用,而thread内部while(true)是死循环,线程不会停止,对外部activity的强引用也不会消失。这样就造成了 memory leak,内存溢出。通常情况下我们可以设置一个flag,在activity,的生命周期ondestroy中改变flag的状态,但是!!!主线程和子线程的执行时有线程调度的,也就是说会造成竞争的现象,两个线程会争夺cup时间片的执行权,额。。越挖越深,这又会造成我们虽然改变的flag的状态,但是,子线程中的flag并不是马上就能改变值,因为jvm memory model,原型和执行原理,所有的线程都是在自己的工作内存中工作的,对值得操作是先从主内存读取到工作线程的工作内存中,然后cpu对工作内存的值进行修改,最后再写回主内存,所以主线程对flag的操作可能恰好的发生在子线程已经读完主内存的值到工作内存,但是还没有执行的这段时间,所以,我们应该让flag的状态改变能够让子线程马上可见,应该在声明flag的时候加上volatile关键字,然该关键字不能保证操作的原子性,但是能够保证变量flag的可见性。当然,还有,我们可以声明一个静态类。但是!!!慎用静态类与静态变量,比如当我们旋转屏幕,可能会造成activity的销毁与重建(虽然国内很多应用不允许旋转屏幕),不希望重新加载图片,所以,有些人会这样写private static Drawable background;然后,这样:ImageView imageView = new ImageView(MainActivity.this);

imageView.setBackground(background);很聪明,这样无论旋转多少次屏幕,bitmap都不会重新加载了,省去了大量的时间,但是!!!imageView 引用了MainActivity的context。imageView.setBackground(background);这句源码中(父类View中)background.setCallback(this);background又引用了imageView的回调强引用,这样旋转屏幕,就会造成之前的MainActivity不能被销毁和回收,他的生命甚至和static一样长,所以,内存泄露了。记得Android官方博有个经典的例子和这个很像。当然,还有很多情况会造成内存泄露,譬如,我们经常这样声明一个匿名内部handler:private Handler handler newHandler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};和匿名内部线程thread一样,会造成内部泄露的隐患。

展开阅读全文

没有更多推荐了,返回首页