内存泄漏
- LeakCanary,Square公司开源的第三方内存检测工具
- 内存泄露VS内存溢出
内存溢出:超出了虚拟机分配的内存
内存泄露:某个不再使用的对象被其他实例引用,导致其不能被回收,内存泄露有可能引起内存溢出
内存泄漏原理
- 长生命周期对象,持有了短生命周期对象的引用,导致了短生命周期对象无法被回收
1.单例引起的内存泄漏
public class SingleInstance {
private Context context;
private static SingleInstance instance;
private SingleInstance(Context context) {
this.context = context;
}
public static SingleInstance getInstance(Context context) {
if (instance == null) {
instance = new SingleInstance(context);
}
return instance;
}
}
/**
* 创建一个单例的时候,会传入一个context
*/
/**
* 如果传入的context是activity的话
* 在activity退出的时候,单例对象持有了activity的引用,导致activity不能被回收
* 长生命周期对象,持有了短生命周期对象的引用,导致短生命周期对象在想要被回收的时候无法被回收。
*/
SingleInstance instance1 = SingleInstance.getInstance(MainActivity.this);
/**
* 传入getApplicationContext()可以解决这个问题
* Application的生命周期就代表了整个应用的生命周期,
* 单例的静态属性导致了单例的生命周期也是整个应用
*/
SingleInstance instance2 = SingleInstance.getInstance(getApplicationContext());
2.Handler引起的内存泄漏
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
public class Main2Activity extends AppCompatActivity {
/**
* 非静态内部类,Handler会持有外部类的引用,
*/
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Message message = new Message();
message.what = 1;
/**
* 消息延时10分钟发送
* 同时message持有了handler的引用
* 而handler是非静态内部类持有了Activity的引用,导致GC无法回收activity
*/
handler.sendMessageDelayed(message, 60 * 10 * 1000);
}
}
- 解决Handler的内存泄露
1.避免使用handler的非静态内部类
2.通过弱引用来引用activity
3.使用静态引用
4.销毁的时候,移除掉Message
public class NoLeakActivity extends Activity {
//3.使用静态引用
private static NoLeakHandler handler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new NoLeakHandler(NoLeakActivity.this);
Message message = Message.obtain();
message.what = 1;
handler.sendMessageDelayed(message, 10 * 60 * 1000);
}
//4.activity销毁时,移除所有message
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
//1.使用静态内部类
private static class NoLeakHandler extends Handler {
//2.在需要传入Activity引用的时候使用弱引用
private WeakReference<NoLeakActivity> mActivity;
public NoLeakHandler(NoLeakActivity activity) {
this.mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
}
}
}
}
3.线程引起的内存泄漏
- AsyncTask,Runnable,Thread
如果以匿名内部类的形式创建的话,他们就会持有当前Activity的隐式的引用,如果Activity在被销毁之前,AsyncTask,Thread中的耗时任务没有执行完,导致Activity不能被回收。 - 解决办法和Handler类似:
1.使用静态内部类的方式,避免内存泄露
2.在Activity被销毁的时候,取消掉AsyncTask中的任务
4.WebView引起的内存泄漏
- 打开新页面的时候,为了能够快速回退,之前页面占用的内存也不会被释放掉,加载网页较多的时候,将会导致系统不堪重负。在onDestroy中销毁webview也是无效的。
- 解决方法:
将webView放在一个单独的进程中。
其他内存优化的方法:
- 1.当service完成后,尽量停止他。(推荐IntentService)
可以用IntentService来代替Service。IntentService继承自Service,Service在主线程,不能进行耗时操作。IntentService内部会开启一个子线程来执行耗时操作。IntentService完成后会自动退出,而Service需要手动调用stopService来退出。如果开发人员忘记了关闭Service将会造成内存的泄露。 - 2.使用多进程。
定位服务,webview,push等,开启单独的进程。