一.内存泄漏的本质:
长生命周期的对象持有短生命周期对象的引用,导致GC无法回收短生命周期的对象。
二.Android中常见的内存泄漏的场景:
1.集合类:
全局集合类强引用没清理会造成内存泄漏(特别是 static 修饰的集合)。
2.静态成员变量:
静态成员变量的生命周期=应用程序的生命周期,如果静态成员变量持有生命周期短的对象引用,会导致内存泄漏。
3.单例类:
单例类的生命周期=应用程序的生命周期,如果单例持有生命周期短的对象引用,会导致内存泄漏。
(1)单例持有了activity的引用。
3.非静态内部类/匿名内部类:
非静态内部类/匿名内部类会持有外部类的引用,如果非静态内部类/匿名内部类的生命周期比外部类的生命周期长的话,会导致内存泄漏。
(1)非静态/匿名Handler持有activity的引用。
(2)非静态/匿名Thread持有activity的引用。
4.资源类:
资源对象用完没关闭造成的内存泄漏、监听器等注册之后没有解注册。eventsbus。
(1)广播没有解注册
//广播的注册和解注册要成对并且要在对应的生命周期进行注册/解注册
unregisterReceiver(receiver);
(2)文件流没有关闭
inputStream / outputStream.close()
(3)cursor(游标)没有关闭
cursor.close
(4)图片资源没有回收
//图片资源在使用完要记得回收
biamap.recycler();
bitmap=null;
(5)动画资源启动之后没有停止
animation.cancel();
5.其它
(1)Activity 的 Context 造成的泄漏,可以使用 ApplicationContext。
三.Android中常见的内存泄漏的案例分析:
1.集合类:
如案例所示,只是简单地将使用对象使用完之后置空是不行的,还需要将这个集合中的元素在集合不需要使用的时候里面元素置空以及集合置空。
public class MainActivity extends AppCompatActivity {
//集合类,尤其是static的
static List<Object> testList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//往集合类中存放数据
for (int i = 0; i < 10; i++) {
Object object = new Object();
testList.add(object);
//这里将对象在使用完之后置空
object = null;
}
}
}
改进如下:
public class MainActivity extends AppCompatActivity {
//集合类,尤其是static的
static List<Object> testList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//往集合类中存放数据
for (int i = 0; i < 10; i++) {
Object object = new Object();
testList.add(object);
//这里将对象在使用完之后置空
object = null;
}
//在集合使用完毕之后,需要对集合以及集合内元素进行置空
testList.clear();
testList = null;
}
}
3.非静态内部类/匿名内部类:
(1)非静态/匿名Handler持有外部activity的引用。
如案例所示,由于匿名内部类/非静态内部类默认持有外部类的引用,会导致外部类即使在不使用的情况下也没办法释放。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//匿名内部类
Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
// TODO: 2024/3/18 0018
}
};
//非静态内部类
private class TestHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
// TODO: 2024/3/18 0018
}
}
}
改进方案1:Activity销毁时,清空Handler中未执行或正在执行的Callback以及Message。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//匿名内部类
Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
// TODO: 2024/3/18 0018
}
};
@Override
protected void onDestroy() {
super.onDestroy();
//移除所有的callBack和message
handler.removeCallbacksAndMessages(null);
}
}
改进2:采用静态内部类+弱引用的写法。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//非静态内部类
private static class TestHandler extends Handler {
//虚引用
WeakReference<Activity> mActivity;
//构造函数
TestHandler(Activity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
// TODO: 2024/3/18 0018
}
}
}
改进3:将非静态内部类抽出去写一个类
四.如何防止内存泄漏:
1、尽量使用Application的Context而不是Activity的
2、使用弱引用或者软引用
3、手动设置null,解除引用关系
4、将内部类设置为static,不隐式持有外部的实例
5、注册与反注册成对出现,在对象合适的生命周期进行反注册操作。
6、如果没有修改的权限,比如系统或者第三方SDK,可以使用反射进行解决持有关系
7、在使用完BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源时,一定要在Activity中的OnDestry中及时的关闭、注销或者释放内存