转载自:http://www.jianshu.com/p/9429c35b9571
内存泄漏通常是因为存在着某个对象的引用,而实际上这个对象未来已经不打算使用导致的。
让我们先从一个简单的例子开始:
public class LeakActivity extends Activity {
public static Context context;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
context = this;
}
}
这是一个非常简单的内存泄漏的例子,这个Activity被public static Context context引用,导致这个Activity无法被回收。
内存泄漏是因为有变量一直持有这个对象的引用
有个这个简单的例子作为基础,让我们接下来讨论一个稍微有点难度的例子:
public class LeakActivity extends Activity {
static SQLiteOpenHelper sqLiteOpenHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
sqLiteOpenHelper = new SQLiteOpenHelper(this, "", null, 33) {
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
};
}
}
我们要看这段代码会不会导致内存泄漏,关键要看activity的对象传到SQLiteOpenHelper的构造函数里面之后,这个类是怎么处理这个activity对象的。实际上SQLiteOpenHelper内部把activity作为context保存为了这个类的一个字段,所以只要SQLiteOpenHelper的对象存在,就会一直持有这个Context,导致Activity无法被回收。
SQLiteOpenHelper会持有Context的引用,导致Context无法被回收。
但是仅仅把第一个参数由this改为getApplicationContext()是没用的,这段代码依旧会造成内存泄漏,为了解释这个问题,我们再来看一个例子:
public class LeakActivity extends Activity {
static Runnable runnable;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
runnable = new Runnable() {
@Override
public void run() {
}
};
}
}
这段代码会造成内存泄漏,使得这个Activity无法被释放,因为实现了Runnable接口的匿名内部类,持有了外部类对象的引用。为了证明这个理论,我们可以来看一下这个匿名内部类反编译后的结果:
class LeakActivity$2 implements Runnable {
LeakActivity$2(LeakActivity this$0) {
this.this$0 = this$0;
}
public void run() {
}
}
在这个匿名内部类的构造函数里面,传进来了外部对象的引用,并且赋值给了自己类内部的字段this$0。
匿名内部类会持有外部类的引用,导致外部类无法被释放
还有一种情况比较相似,但是却不会导致内存泄漏的情况:
public class LeakActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
}
static Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
}
这段代码的匿名类初始化是在这个类被加载的时候,那个时候还不存在这个类的对象,也就不存在外部类的对象被内部类持有的情况。我们来看一下这个时候这个匿名内部类反编译的结果:
final class LeakActivity$2 implements Runnable {
LeakActivity$2() {
}
public void run() {
}
}
确实像我们预测的那样,并没有持有外部类的引用。
但是如果我们去掉static关键字,这种情况下,匿名内部类会持有外部类的引用
public class LeakActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leak);
}
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
}