1,内存泄漏到本质是该释放的对象被持久化的对象引用了,造成持久化的常见情况有1,静态持久化 2,线程持久化
线程持久化
因为存活的线程是有dvk虚拟久直接持有,所以存活的线程都是持久化的
内存泄漏1:静态Activities(static Activities)
代码如下:
MainActivity.Java
public class MainActivity extends AppCompatActivity { private static MainActivity activity; TextView saButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); saButton = (TextView) findViewById(R.id.text); saButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticActivity(); nextActivity(); } }); } void setStaticActivity() { activity = this; } void nextActivity(){ startActivity(new Intent(this,RegisterActivity.class)); SystemClock.sleep(1000); finish(); } @Override protected void onDestroy() { super.onDestroy(); //使用LeakCanary观察是否有内存泄漏 MyApplication.getRefWatcher().watch(this); } }
LeakCanary检测出的内存泄漏:
为什么?
在上面代码中,我们声明了一个静态的Activity变量并且在TextView的OnClick事件里引用了当前正在运行的Activity实例,所以如果在activity的生命周期结束之前没有清除这个引用,则会引起内存泄漏。因为声明的activity是静态的,会常驻内存,如果该对象不清除,则垃圾回收器无法回收变量。
怎么解决?
最简单的方法是在onDestory方法中将静态变量activity置空,这样垃圾回收器就可以将静态变量回收。
@Override
protected void onDestroy() {
super.onDestroy(); activity = null; //使用LeakCanary观察是否有内存泄漏 MyApplication.getRefWatcher().watch(this); }
不使用静态activity,或给静态activity赋值时,考虑赋值的activity生命周期是不是全局的,或者在静态activity使用完后及时释放
内存泄漏2:静态View
代码如下:
MainActivity.java
...
private static View view;
TextView saButton;
@Override
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); saButton = (TextView) findViewById(R.id.text); saButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setStaticView(); nextActivity(); } }); } void setStaticView() { view = findViewById(R.id.sv_view); } ...
LeakCanary检测到的内存泄漏
为什么?
上面代码看似没有问题,在Activity里声明一个静态变量view,然后初始化,当Activity生命周期结束了内存也释放了,但是LeakCanary却显示出现了内存泄漏,为什么?问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。
怎么解决?
在onDestroy方法里将静态变量置空。
@Override
protected void onDestroy() {
super.onDestroy(); view = null; MyApplication.getRefWatcher().watch(this); }
不使用静态view,或在activity关闭时将静态view赋值为null
内存泄漏3:静态内部类
代码如下:
MainActivity.java
private static Object inner;
void createInnerClass() {
class InnerClass {
}
inner = new InnerClass();
}
View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { createInnerClass(); nextActivity(); } });
使用LeakCanary检测到的内存泄漏:
为什么?
非静态内部类会持有外部类的引用,在上面代码中内部类持有Activity的引用,因此inner会一直持有Activity,如果Activity生命周期结束没有清除这个引用,这样就发生了内存泄漏。
怎么解决?
因为非静态内部类隐式持有外部类的强引用,所以我们将内部类声明成静态的就可以了。
void createInnerClass() {
static class InnerClass {
}
inner = new InnerClass();
}
内存泄漏3:静态Drawable
当一个Drawable附加到一个 View上时,
View会将其作为一个callback设定到Drawable上。意味着Drawable拥有一个View的引用,上面说了view会有上下文的引用
内存泄漏4:静态集合中对象没清理造成的内存泄漏
我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
内存泄漏5:单例导致内存泄露
单例模式在Android开发中会经常用到,但是如果使用不当就会导致内存泄露。因为单例的静态特性使得它的生命周期同应用的生命周期一样长,如果一个对象已经没有用处了,但是单例还持有它的引用,那么在整个应用程序的生命周期它都不能正常被回收,从而导致内存泄露。
public class AppSettings {
private static AppSettings sInstance; private Context mContext; private AppSettings(Context context) { this.mContext = context; } public static AppSettings getInstance(Context context) { if (sInstance == null) { sInstance = new AppSettings(context); } return sInstance; } }
像上面代码中这样的单例,如果我们在调用getInstance(Context context)
方法的时候传入的context
参数是Activity
、Service
等上下文,就会导致内存泄露。
以Activity
为例,当我们启动一个Activity
,并调用getInstance(Context context)
方法去获取AppSettings
的单例,传入Activity.this
作为context
,这样AppSettings
类的单例sInstance
就持有了Activity
的引用,当我们退出Activity
时,该Activity
就没有用了,但是因为sIntance
作为静态单例(在应用程序的整个生命周期中存在)会继续持有这个Activity
的引用,导致这个Activity
对象无法被回收释放,这就造成了内存泄露。
为了避免这样单例导致内存泄露,我们可以将context
参数改为全局的上下文:
private AppSettings(Context context) {
this.mContext = context.getApplicationContext(); }
全局的上下文Application Context
就是应用程序的上下文,和单例的生命周期一样长,这样就避免了内存泄漏。
单例模式对应应用程序的生命周期,所以我们在构造单例的时候尽量避免使用Activity
的上下文,而是使用Application
的上下文。