android内存泄漏与解决方案

应用场景

android系统为每个应用分配的java object heap都是固定的,如果我们不及时释放废弃资源所占用的内存空间,那么就会使我们的程序运行起来非常卡顿,当超过系统为我们分配的内存空间时,系统就会抛出OOM

内存泄漏原因

我们开发者不需要它存在的一些资源,但是因为种种原因,这部分资源不能够被GC回收掉,这便会造成内存泄漏,通俗点讲是“该死不死”,下面我将对这“种种原因”来做一下分析

Activity内存泄漏

原因

当activity被废弃销毁时,因为activity中的部分对象还持有对activity的引用,这使得GC没办法对废弃的activity的一个回收。

解决方案

1.不要让生命周期长的对象引用activity context,既保证引用activity的对象要与activity本身的生命周期一样长。

2.比如,如果引用context的对象声明周期长于activity的那么最好选用application context代替。

3.应注意handler的使用,一般大家使用的时候采用非静态匿名内部类的使用方式,如果handler长于activity的生命周期时,非静态内部类持有外部类的引用,这时便会造成内存泄漏,我们应该将其写成静态内部类,并弱引用activity对象。

代码示下

public class MainActivity extends Activity {  
  
    private TextView tvHelloWorld;  
    private Button btnSetText;  
    private Handler mHandler = new InternalHandler(this);  
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        btnSetText = (Button) findViewById(R.id.btn_set_text);  
        tvHelloWorld = (TextView) findViewById(R.id.tv_hello_world);  
        btnSetText.setOnClickListener(new OnClickListener() {  
  
            @Override  
            public void onClick(View v) {  
                mHandler.post(new Runnable() {  
  
                    @Override  
                    public void run() {  
                        tvHelloWorld.setText("Runnable");  
                    }  
  
                });  
            }  
  
        });  
    }  
  
    private static class InternalHandler extends Handler {  
        private WeakReference<Activity> weakRefActivity;  
  
        /** 
         * A constructor that gets a weak reference to the enclosing class. We 
         * do this to avoid memory leaks during Java Garbage Collection. 
         */  
        public InternalHandler(Activity activity) {  
            weakRefActivity = new WeakReference<Activity>(activity);  
        }  
  
        @Override  
        public void handleMessage(Message msg) {  
            Activity activity = weakRefActivity.get();  
            if (activity != null) {  
  
            }  
        }  
    }  
}


同样,AsyncTask内部也是Handler机制,同样也存在相同问题。

4.同样也要注意,非静态内部类的静态实例化对象,然后此静态实例化对象被外部比activity生命周期长的对象所持有同样也会造成内存泄漏问题,这种造成内存泄漏的机制与本文第三条基本相同。我们在不用时应注意要将静态变量置为null,这样便于GC的回收。

5.注意activity被静态集合引用导致activity不能释放。

6.在activity中使用Thread、Timer Tasks、广播接收者、Sensor Manager等要在onDestroy方法中释放掉。

资源文件泄漏

原因

当使用了BraodcastReceiver、ContentObserver、Cursor、Bitmap、File等资源时,当不需要使用时,需要及时释放掉,若没有释放,则会引起内存泄漏。

解决方案

1.BraodcastReceiver的注册与反注册

//注册
registerReceiver(mFinishReceiver, filter);
//反注册
unregisterReceiver(mFinishReceiver);

2.

ContentObserver的注册与反注册

//注册
getContentResolver().registerContentObserver(uri, true, observer);
//反注册
getContentResolver().unregisterContentObserver(observer);



3.Cursor

Cursor cursor = null;
try{
    cursor = mContext.getContentResolver().query(uri,null,null,null,null);
    if(cursor != null){
        cursor.moveToFirst();
    //do something
    }
}catch(Exception e){
    e.printStatckTrace();
}finally{
    if(cursor != null){
        cursor.close();
    }
}

有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭。

@Override
protected void onDestroy() {
    if (mAdapter != null && mAdapter.getCurosr() != null) { 
        mAdapter.getCursor().close();  
    }
    super.onDestroy();   
}

4.Bitmap

Bitmap bit = BitmapFactory.decodeFile(path);  
if(bit != null && !bit.isRecycled()) {  
    bit.recycle();  
}

5.File


File file = new File("/home/admin/a.txt");  
        FileOutputStream  out = null;  
        try {  
            out = new FileOutputStream(file);  
            file.delete();  
            while(true){  
                try {  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            try {  
                if(out!=null) {  
                    out.close();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
}

单例造成的内存泄漏

原因

单例模式非常受开发者的喜爱,不过使用的不恰当的话也会造成内存泄漏,由于单例的静态特性使得单例的生命周期和应用的生命周期一样长,这就说明了如果一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,这就导致了内存泄漏。如下这个典例:

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;
	}
}

总结

归根结底,内存泄漏问题就是不用的资源仍然占据着内存资源,因为我们不合理的代码结构导致这些被废弃的仍然被持有,GC无法进行回收。也就是我们上面分析的常见的场景,但是我们也要注意static的使用毕竟它是常驻内存的。