内存泄漏
什么是内存泄漏
内存泄漏(Memory Leak)
:是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
比如:当Activity的onDestroy()
方法被调用后,Activity
以及它涉及到的View和相关的Bitmap
都应该被回收掉。但是,如果有一个后台线程做耗时操作,导致生命周期比Activity长,造成GC
无法回收Activity,就造成内存泄漏。
内存泄漏后果
它是造成应用程序OOM
的主要原因之一。由于android
系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时,就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就会导致程序崩溃等严重后果。
检测工具
最常见的是:Leakcanary
leakCanary
是Square
开源框架,是一个Android和Java的内存泄露检测库,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知,所以可以把它理解为傻瓜式的内存泄露检测工具。通过它可以大幅度减少开发中遇到的oom问题,大大提高APP的质量。
常见的内存泄漏
单例造成的内存泄漏
单例在Android中经常使用,如果使用不当会造成内存泄漏,因为单例的静态特性使得他的生命周期与应用的生命周期一样长,这就造成当前对象的生命周期比单例短,单例又持有该对象的引用,GC无法回收该对象,就这导致内存泄漏,比如Context
使用不当: 这里的Context如果使用的是activity的Context,造成单例持有activity的引用,它的生命周期又是整个应用的生命周期,导致activity无法销毁。则有可能造成内存泄漏,比如:
public class LoginManager {
private static LoginManager mInstance;
private Context mContext;
//问题代码
private LoginManager(Context context) {
this.mContext = context;
//修改代码:this.mContext = context.getApplicationContext();
}
public static LoginManager getInstance(Context context) {
if (mInstance == null) {
synchronized (LoginManager.class) {
if (mInstance == null) {
mInstance = new LoginManager(context);
}
}
}
return mInstance;
}
public void get() {
}
}
场景: 在一个Activity
中调用的,然后关闭该Activity则会出现内存泄漏。 LoginManager.getInstance(this).get();
解决方案: 可以将Context
修改为AppLication的Context
,代码如下: **this.mContext = context.getApplicationContext();**
此时传入的是 Application
的 Context
,因为 Application
的生命周期就是整个应用的生命周期,此时Context就和整个应用的生命周期一样,与单例的生命周期一样,单例持有的是整个application
的引用,与activity无关,此时activity就正常可以销毁了,所以这将没有任何问题。
非静态内部类造成的内存泄漏
比如:
public class TestLeak extends AppCompatActivity {
private static InnerClass mInnerClass = null;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_test);
if (mInnerClass == null) {
mInnerClass = new InnerClass();
}
}
class InnerClass {
}
}
代码中有一个非静态内部类InnerClass
,有一个静态变量mInnerClass
,在onCreate中进行了初始化操作,这个内部类实例就持有activity的强引用,而静态变量的生命周期与应用的生命周期一样长,而activity的生命周期比它短,想要销毁时,被持有引用,无法回收,继而造成内存泄漏。 解决方案:
- 将内部类设置为静态内部类,静态内部类不会持有外部类的引用,比如: public class TestLeak extends AppCompatActivity { private static InnerClass mInnerClass = null; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.act_test); if (mInnerClass == null) { mInnerClass = new InnerClass(); } } static class InnerClass { } }
- 将该内部类抽取出来封装成一个单例,使用
Application
的Context。 public class InnerClass { private volatile static InnerClass instance; private InnerClass(Context context) { } public static InnerClass getInstance(Context context) { if (instance == null) { synchronized (InnerClass.class) { if (instance