1.单例造成的内存泄露
单例的生命周期与应用一样长,因此当创建出来后就会一直存在,如果在创建的时候持有了某个对象的引用,就会一直持有它导致内存泄露。如下面的例子,在创建ActivityManager单例的时候我们传入了一个上下文Context参数,包含了一个对Activity的引用,那么在这种情况下就会造成该Activity无法回收,发生内存泄露
public class ActivityManager
{
private Context mContext;
private static ActivityManager manager;
private ActivityManager(Context mContext)
{
this.mContext = mContext; //持有了Activity的Context
}
public static ActivityManager getInstance(Context mContext)
{
if (manager!=null)
{
manager = new ActivityManager(mContext);
}
return manager;
}
}
修正方案为,通过传入的Activity的Context参数获取到ApplicationContext,这样这个单例持有的就是应用本身的引用,本身单例就与应用生命周期相同,因此就不会有内存泄露发生。
2.Handler造成的内存泄漏
public class DemoActivity extends AppCompatActivity
{
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//...更新UI操作
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
initDatas();
}
private void initDatas()
{
//...子线程获取数据,在主线程中更新UI
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
Handler是Activity中的非静态匿名内部类,因此创建的mHandler会持有外部对象DemoActivity的引用,looper会在Activity同一线程不断循环查询消息并进行处理,当Activity退出时如果还有未处理完成的消息,消息队列会一直持有handler的引用,而handler又一直存在并持有Activity的引用,导致Activity无法被回收,导致内存泄露。
public class DemoActivity extends AppCompatActivity
{
private MyHandler mHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null)
{
//...更新UI操作
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
initDatas();
}
private void initDatas()
{
//...子线程获取数据,在主线程中更新UI
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
//移除消息队列中所有消息和所有的Runnable
mHandler.removeCallbacksAndMessages(null);
}
}
3.匿名内部类造成的内存泄漏(实际上是第二点的泛化情况)
匿名内部类会持有外部对象(如Activity)的引用,当这个匿名内部类对象生命周期与Activity一致时不会出现问题,但是当匿名内部类生命周期超出外部对象(如启动了一个新的线程),则会出现外部对象无法被回收,而导致内存泄露。
例如AscynTask:
void startAsyncTask() {
new AsyncTask<Void, Void, Void>() {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}.execute();
}
解决方案就是将匿名内部类改为一个静态内部类。
private static class NimbleTask extends AsyncTask<Void, Void, Void> {
@Override protected Void doInBackground(Void... params) {
while(true);
}
}
void startAsyncTask() {
new NimbleTask().execute();
}
如果一定要持有外部对象,请将其设置为弱引用。
另外一种解决方案是在将其生命周期与外部对象同步,如匿名内部类启动了一个新的线程,那么我们在外部对象被销毁时终止该线程
private Thread thread;
@Override
public void onDestroy() {
super.onDestroy();
if (thread != null) {
thread.interrupt();
}
}
void spawnThread() {
thread = new Thread() {
@Override public void run() {
while (!isInterrupted()) {
}
}
}
thread.start();
}
4.静态变量导致的内存泄露
public class DemoActivity extends AppCompatActivity
{
private static Context mContext;
@Override
private void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.mContext = this; //静态变量与类的生命周期相同,但持有该Activity实例的引用,造成泄漏
}
}
public class DemoActivity extends AppCompatActivity
{
private static View sView;
@Override
private void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sView = new View(this); //静态变量与类的生命周期相同,但持有该Activity实例的引用,造成泄漏
}
}
该问题主要为类声明的静态变量持有了某个实例的引用,若类不被卸载,则该静态变量不会被回收,同样的,其持有的实例引用也不会被回收,造成内存泄露。
5.杂七杂八的东西
在Activity生命周期技术的时候完成结束动画、置空bitmap、关闭流Stream、关闭游标Cursor、注销BroadcastReceiver等操作。
6.小结
内存泄露的核心实际上都是由于某长生命周期对象持有了较短生命周期对象的引用,所以需要着重注意单例、静态变量、会启动长周期任务的匿名内部类等长周期对象,注意不要让其持有Activity实例的引用。