内存泄漏和内存溢出的区别
- 内存泄漏:对象被创建之后,没有引用到,当时没有被回收,一直占用着内存
- 内存溢出:程序使用的空间大于原本系统给它申请的空间
泄漏场景:
单例引起的内存泄漏
单例模式的生命周期常常伴随程序一生,这也就会有内存泄漏出现的间接原因
比如:单例模式中引用的是Activity的context,而单例的生命周期比Activity的要长,所以当单例引用activity的实例时,activity被销毁,而activity无法被回收,这就造成内存泄漏,eg:
public class Single{
private static Single instance;
private Context mContext;
public Single(Context context) {
mContext = context;
}
public static Single getInstance(Context context){
if (instance == null){
instance = new Single(context);
}
return instance;
}
}
如上面例子,如果单例持有application的context就不会造成内存泄漏,引用application的生命周期和单例一样长
匿名内部内以及非静态内部类
特点:匿名类和非静态内部类都持有外部类的引用
匿名内部类---Handle
匿名内部类造成内存泄漏比较经典的场景就是Handle,当Handle的消息还没有发送完,activity就被销毁了,这时候的activity是无法被回收的
eg:
handle问题.png
如何解决Handler泄漏呢?我们用static修饰Handler,这样Hanlder的生命周期就与Activity无关了。如果想引用Activity实例,这里可以用一个弱引用来获取。或者可以在Activity 的onDestroy() 方法中移除所有的消息
handle解决.png
Thread泄漏
在Activity中new Thread时,如果在子线程做耗时操作,当Activity被销毁后,子线程的工作并未完成,此时会内存泄漏。
threa.png
这里可以使用继承Thread实现静态内部类来解决
Stream未关闭
在调用了流之后,一定要记得关闭流。用到流的地方一般都是文件操作,虚拟机无法通过垃圾回收来释放这些资源。
其他泄漏
例如service忘记解除绑定,broadcastReceiver忘记解除订阅,EventBus忘记解除订阅等。
常用的检测内存泄漏的工具
- Android Lint:Android Studio提供的代码扫描分析工具
- Leakcanary: Square 公司开源的「Android 和 Java 的内存泄漏检测库」