内存泄漏(Memory Leak
)是指某些对象已经不再使用了,但却无法被垃圾回收器回收内存,还一直占用着内存空间的现象,这就导致这一块内存泄露了。
而垃圾回收器无法回收某些对象的原因是因为这些对象还在被引用着。所以内存泄露的本质就是生命周期短的对象被生命周期长的对象引用了,导致生命周期短的对象无法及时被垃圾回收器回收。
少量的内存泄漏并不会产生大的影响,当泄露的内存越来越大,可用的空闲内存越来越小,GC(垃圾回收机制)就会更容易被触发,GC进行时会停止其他线程的工作,因此有可能会造成界面卡顿等情况;
随着内存泄漏的堆积,大量的内存泄漏的堆积就可能会造成内存溢出。
在 Android 应用程序当中,内存泄露要注意以下几个方面
(1)减少 static 使用:被 static 关键字修饰的成员变量的生命周期等于应用程序的生命周期,如果大量的使用的话,就会占据内存空间不释放,积少成多也会造成内存的不断开销;并且应该避免 static 成员变量引用资源耗费过多的实例(如 Context),若需引用 Context 则使用 Applicaiton 的 Context。
(2)避免非静态内部类(匿名内部类)持有外部类引用:非静态内部类(匿名内部类)默认会持有外部类的引用,而静态内部类则不会。Handler、AsyncTask(AsyncTask底层就是Handler)、多线程(实现 Runnable 接口、继承 Thread 类)的内存泄漏就属于这种,这种情况下应使用静态内部类 + 弱引用的方式。
(3)注册后及时反注册:BraodcastReceiver、Service、ContentObserver、EventBus等在注册后一定需要在对应的生命周期方法中进行反注册。
(4)资源对象使用完及时关闭 :数据库操作时的Cursor、读写文件时的IO流 等对象没有关闭;
(5)Bitmap 对象不在使用时应调用 recycle()释放内存:Bitmap 非常的耗费内存,多使用几个 Bitmap 很可能一下子就会超过 Java 堆的限制。因此,在用完 Bitmap 时,要 及时的 recycle 掉。recycle 并不能立即将 Bitmap 的内存释放,但是会在垃 圾回收器下一个工作的时机将这个 bitmap 回收。
(6)单例模式时不引用非 Application 的 Context:由于单例的静态特性使得其生命周期跟应用的生命周期一样长,所以如果使用不恰当引用 Activity 的 Context 的话,很会造成内存泄漏,所以单例模式中的 Context 必须引用 Application 的 Context 。
(1)静态代码分析工具 lint:可以用来检测部分会出现内存泄露的代码,平时编程注意 lint 提示的各种黄色警告即可。
(1)Android Studio 中自带的 Android Profiler:Android Studio 3.0 及更高版本中的 Android Profiler 取代了 Android Monitor 工具。
(2)开源的 leakcanary:。
内存溢出(Out Of Memoery:OOM)是指应用程序申请使用的内存超过的系统规定的最大值。Android系统对每个应用程序能够使用的内存都有一个最大值限定,当内存申请超出这阈值时就会出现OOM Crash。
早期使用的 ListView 需要手动进行 item 的复用,否则在快速滑动时生成大量的 ItemView,垃圾回收器又来不及回收内存,就可能出现OOM(这也是 ListView 被 RecyclerView 替代的主要原因);
a、加载多张图片时:对于加载多张图片,我们一般会使用三级缓存(内存缓存、本地缓存、网络缓存)来实现图片的加载。使用缓存可以使下一次加载时速度更快,所以在内存中保留着一定数量的图片缓存有助于下一次图片显示的速度更快。但是内存中不能保存太多的位图对象,所以一般使用 LRUCache 算法来保存内存中的图片,并且控制在一定的数量之内。
- BitmapFactory.Options options = new BitmapFactory.Options();
- //只得到图片的大小,不去加载图片的内容
- options.inJustDecodeBounds = true;
- Bitmap boundBitmap = BitmapFactory.decodeFile(filePath,options);
- int imageWidth = boundBitmap.getWidth();
- int imageHeight = boundBitmap.getHeight();
- //计算 inSampleSize 的值 此时可以根据控件大小和图片大小来得到缩放比例 这里先写成 2
- options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
- options.inJustDecodeBounds = false;
- Bitmap bitmap = BitmapFactory.decodeFile(filePath,options);