1、android中的内存泄露
对于android中一些无用的对象,比如activity,view长期持有这些对象,导致无法被GC回收利用,再次使用的,重新申请内存。长久下去,应用程序无法分配新内存,导致OOM。
大部分内存泄露,不会直接导致程序崩溃,但长期下去,应用程序无法负担。一反面使用app耗用大量内存,卡顿,别一方面。最终还是可能导致OOM导常。
square比较好用的内存检测工具
https://github.com/square/leakcanary
2、android中常见的内存泄露地方
1、不合理的使用static静态变量。
2、不合理的使用单例。
3、handler引起的内存泄露。
4、查询数据库,对文件读写。未关闭资源(cursor没有关闭)。
5、不合理的监听(该对象已经移除,还对该对象进行监听)比如,内容观察者没有在适合的地方移除。
6、内部类的不适当使用。
这几个地方,在android开发的过程中,经常遇到。因此在这几个地方,要特别注意,防止发生内存泄露。
3、慎用static关键字
使用静态static静态变量潜在性问题:
1.占用内存,并且内存一般不会释放;
2.在系统不够内存情况下会自动回收静态内存,这样就会引起访问全局静态错误。
3.不能将activity作为static静态对象,这样使activity的所有组件对象都存入全局内存中,并且不会被回收;
1、静态集合引发内存泄露
在某些情况下可能需要静态集合做些缓存数据,但是。我们可能在每次使用的时候,给静态集合中添加了数据,但从没有移除过数据,导致集合中数据越来越多。因此使用静态集合的时候,一定记得在适合的情况下移除集合中的数据,防止内存泄露。
2、单例模式引起内存泄露
public class Singleton {
private static Singleton singleton;
private Context context;
private Singleton(Context context) {
this.context=context;
}
/**
* 单例引起的内存泄露;
* @param context
* @return
*/
public synchronized static Singleton getSingleton(Context context) {
if (singleton == null) {
singleton = new Singleton(context);
}
return singleton;
}
}
由于单例拥持有了当前Activity对象,所以当前Activity没有办法回收,导致MainActivity内存泄露。
解决办法:把context换成application的context;
public class Singleton {
private static Singleton singleton;
private Context context;
/**
* 把生命周期更长的application的上下文传入
*
* @param context
*/
private Singleton(Context context) {
this.context = context.getApplicationContext();
}
/**
* 单例引起的内存泄露;
*
* @param context
* @return
*/
public synchronized static Singleton getSingleton(Context context) {
if (singleton == null) {
singleton = new Singleton(context);
}
return singleton;
}
}
3、Handler引起的内存泄露
android handler机制用于我们从子线程返回的数据后,刷新ui。但在一些情况下,后台仍然在执行耗时操作(网络请求,数据库查询等)时,当前activity已经销毁,被回收,而我们还在等待返回数据后ui刷新。当前的handler持有对activity的引用,因此,activity也没有办法销毁。
public class SecondActivity extends AppCompatActivity {
private TextView textView;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
textView = (TextView) findViewById(R.id.text);
mHandler=new MyHandler();
Message message = new Message();
message.arg1 = 1;
mHandler.sendMessageDelayed(message, 5000);
finish();
}
class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
textView.setText("123");
break;
}
}
}
}
解决办法:
public class SecondActivity extends AppCompatActivity {
private TextView textView;
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
textView = (TextView) findViewById(R.id.text);
mHandler=new MyHandler(this);
Message message = new Message();
message.arg1 = 1;
mHandler.sendMessageDelayed(message, 5000);
finish();
}
public void notifyView() {
textView.setText("12");
}
private static class MyHandler extends Handler {
private final WeakReference<SecondActivity> mActivity;
public MyHandler(SecondActivity activity) {
mActivity = new WeakReference<SecondActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
System.out.println(msg);
if (mActivity.get() == null) {
return;
}
mActivity.get().notifyView();
}
}
}
在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。
android中的很多内存泄露都是由于在Activity中使用了非静态内部类导致的,我们在使用非静态内部类一定要格外注意,如果该静态内部类的实例对象的生命周期大于外部对象,那么就有可能导致内存泄露,推荐使用上面介绍的静态类和弱引用的方法解决这种问题。
4、LeakCanary内存泄露工具分析
leakcanary的确很大程度帮我们找出内存泄露的地方,方便的们及时排查内存泄露的代码。
leakcanary其实相当于把所有activity(Application)的生命周期回调放在一个集合中,这个集合存放activity回调,leakcanary对activity的onDestory方法进行检测。
Application的ActivityLifecycleCallbacks回调接口中的onDestory()回调实现对项目中的activity进行对象监听,然后交给RefWatcher监听类进行进一步处理。
LeakCanay的入口是在application的onCreate()方法中声明的,其实用的就是Application的ActivityLifecycleCallbacks回调接口监听所有activity的onDestory()的,在这个方法进行RefWatcher.watch对这个对象进行监控。
具体是这样做的,封装成带key的弱引用对象,然后GC看弱引用对象有没有回收,没有回收的话就怀疑是泄漏了,需要二次确认。然后生成HPROF文件,分析这个快照文件有没有存在带这个key值的泄漏对象,如果没有,那么没有泄漏,否则找出最短路径,打印给我们,我们就能够找到这个泄漏对象了。