内存泄漏的主要原因
- 生命周期长的对象持有了生命周期短的对象,导致生命周期短的对象无法释放内存。(即长的不能持有短的。)
context
情形
情景描述
- 生命周期长的对象保留了生命周期短的
context
,如 单例类 保留了Activity
的context
。
解决方案
- 在获取到生命周期短的
context
时,不保留此context
,而通过Context.getApplicationContext()
获取应用程序的context
。由于保留应用程序的context
的对象生命周期不如应用程序长,所以不会出现问题。
用static
存储内部类对象
情景描述
-
mResource
的生命周期是跟应用程序一样长的,而且由于TestResource
是 内部类 ,会保留 外部类 对象的引用(即Activity
),所以Activity
无法释放,内存泄漏。
public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mManager = new TestResource();
}
//...
}
class TestResource {
//...
}
}
解决方案
把 内部类(Inner Class) 改为 嵌套类(Nested Class) 。
线程相关
情景描述
- 都是因为 内部类 对象(此处为 匿名内部类 )保留了 外部类 对象的引用导致的。
情景一
// 无法保证此匿名内部类所持有的外部类对象的生命长于10秒
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
情景二
// 无法保证此段代码的匿名内部类所持有的外部类对象的生命长于10秒
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
解决方案
- 改用 嵌套类(Nested Class) ,如果需要 外部类 的引用,则通过 构造方法 传递进去,然后使用 弱引用 保留。
Handler情形
情景描述
-
线程 保留了
Looper
的引用。 -
Looper
保留了MessageQueue
的引用。 -
MessageQueue
保留了Message
的引用。 -
Message
保留了Handler
的引用。 - 导致
Handler
的生命周期可能与 线程 一样长。 - 若此时同时满足以下两个条件:
-
Handler
为 内部类(Inner Class,包括匿名内部类) ,即会保留 外部类 对象的引用。 -
外部类 对象的生命周期短于 线程 ,如
Activity
。
-
- 就会导致此 外部类 对象在 线程 释放之前无法释放,引发 内存泄漏 。
解决方案
思路一:破坏条件1
- 让
Handler
成为 嵌套类(Nested Class) ,若要访问原来的 外部类 ,就只需要使用 弱引用 来保留 外部类 对象的引用。
// 将Handler定义为嵌套类(static内部类)以避免内存泄漏。
private static class RequestHandler extends Handler {
private WeakReference<Activity> mWeakRef;
// 可以在构造函数中传入外部类对象,用WeakReference保留此对象的引用,这样可以避免内存泄漏。
public RequestHandler(Activity activity) {
mWeakRef = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
if (msg.what == /* 消息 */) {
// 先判断WeakReference指向的对象还存在。
if (mWeakRef.get() == null) {
return;
}
// 使用WeakReference.get()来获取对象,并使用
Activity activity = mWeakRef.get();
}
}
}
思路二:破坏条件2
-
外部类 的对象的生命周期不短于 线程 ,例如
Activity
就不能作为Handler
的 外部类 ,而 线程 可以。
资源未释放
- 对于使用了
BraodcastReceiver
,ContentObserver
,File
,Cursor
,Stream
,Bitmap
等资源的使用,应该在Activity
销毁时及时关闭或者注销,否则这些资源将不会被回收,造成 内存泄漏 。
LeakCanary
内存泄漏检测工具。