AsyncTask 与内存泄露

       类似如下伪代码事实上可能会造成内存泄漏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyActivity extends AppCompatActivity {

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

new MyAsyncTask().execute(?,?,?);
}

class MyAsyncTask extends AsyncTask{
@Override
protected Object doInBackground(Object[] objects) {
return null;
}
}
}

       所有的内部对象都肯定持有外部对象的引用,比如我们可以在 doInBackground 中调用 onCreate 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MyActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
}

class MyAsyncTask extends AsyncTask{
@Override
protected Object doInBackground(Object[] objects) {
onCreate();
return null;
}
}
}

       MyAsyncTask 是 MyActivity 的内部类,持有 MyActivity 的引用,所以可以调用 MyActivity 的 onCreate 方法。

       Java 回收策略为:没有被 GC Root 直接或间接持有引用的对象,会被回收。GC Root 有以下三种:

  1. 运行中的线程;
  2. 静态对象,所有的 static 修饰的都不会被回收;
  3. 来自 native code 中的引用。

       AsyncTask 中的线程可能在运行过程中,此时 Activity 退出了,由于 AsyncTask 中的线程在运行中,持有 Activity,Activity 释放不掉,造成内存泄露。AsyncTask 会导致内存泄露是因为它里面有线程,AsyncTask 的内存泄露,其它类型的线程方案(Thread、Executor、HandlerThread)一样都有,使用 AsyncTask,只要任务的时间不长,例如10秒之内,那就完全没必要做防止内存泄露的处理,最多是虚拟机晚了10秒回收。但如果 AsyncTask 的时间过长,例如几个小时,那事实上不应该放在 Activity 中启动该任务,需要放入一个单独的后台线程中。

参考资料:
腾讯课堂 HenCoder

Fork me on GitHub