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的使用毕竟它是常驻内存的。