内存泄漏
程序在向系统申请分配内存空间后(new),使用完毕之后没有释放,导致一直占据该内存单元。直到程序结束,我们和程序都无法再使用该内存单元,就是所谓的内存泄露。(其实就是内存空间使用完之后没有回收)内存泄漏是导致程序OOM的主要原因之一。Android系统为每个程序分配的内存有限,如果一个应用产生的内存泄漏较多,就会导致应用所需的内存超过系统分配的内存限额,就造成了内存溢出导致应用Crash。
内存溢出
系统会给每个App分配内存,当App占用的内存加上向系统申请的内存超出了Dalvik虚拟机的最大内存时就会抛出Out Of Memory异常。
强引用
强引用是使用最普遍的引用,垃圾回收器绝不会回收具有强引用的对象。当内存空间不足的时候,Java虚拟机宁愿抛出Out Of Memory异常使程序终止,也不会去回收具有强引用的对象。
软引用
具有软引用的对象,在内存空间足够时,垃圾回收器不会回收它,直到虚拟机报告内存不足的时候才会回收,只要该对象没被回收,程序就可以使用。软引用可以用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果具有软引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
弱引用
垃圾回收器线程扫描它所管辖的区域时,发现只具有弱引用的对象,不管当前的内存空间是否足够,都会把该对象回收掉。垃圾回收器线程是一个优先级很低的线程,不一定会很快发现只具有弱引用的对象。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
虚引用
虚引用可以理解为虚设的引用,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 虚引用主要用来跟踪对象被垃圾回收器回收的活动。
几种内存泄漏
1.单例造成的内存泄漏
当调用getInstance的时候,传入的是外部Activity的Context,如果Activity退出的时候,单例没有被释放,因为单例持有对该Activity的引用,该Activity的内存不会被回收掉,就会造成内存泄漏。
解决方案:不管外部传入什么Context,使用Application的Context,因为Application的生命周期伴随着整个进程的周期。
2.非静态内部类、匿名内部类造成的内存泄漏
非静态内部类、匿名内部类会持有外部类的引用。如果有一个静态变量引用了非静态内部类、匿名内部类,导致非静态内部类、匿名内部类的生命周期比外部类(Activity)长,就会导致外部类在退出时无法被回收,引起内存泄漏。
解决方案:将非静态内部类、匿名内部类声明为静态类。Java中,非静态内部类、匿名内部类会隐式的持有其他外部类的引用,静态的内部类不会持有外部类的引用。静态内部类不吃有外部类的对象,导致不能在Handler中操作Activity的对象,所以需要在Handler中增加一个对Activity的弱引用。
3.Handler造成的内存泄漏
在Activity中使用Handler导致内存泄漏有两种情况。
第一种:2中所说的内部类导致的内存泄漏,改为静态内部类,添加对外部类的弱引用。
第二种:Message间接持有了外部Activity的对象,关闭Activity的时候,如果Message还存在消息队列,Activity就不会被回收。在Activity的onDestory方法中,把未执行完的Message删除,把回调移除。
4.图片导致的内存泄漏
Android默认为图片分配的内存为16M,在大图、多图的场景下,占用的内存超过这个值,就会造成内存泄漏。
解决方案:
①加载缩略图。图片的二次采样。参考 Android图片压缩(二次采样)
②图片的三级缓存。内存、磁盘、网络三级,内存缓存使用LruCache(默认缓存大小:手机内存的1/8),磁盘缓存使用DisLruCache。
5.流、数据库使用完未关闭、bitmap未释放、动画资源未取消
流、数据库、Bitmap使用完及时关闭掉,动画在onDestory中调用cancel取消。