参考:【Android性能优化】内存泄露和内存溢出(OOM)的引发原因及优化方案
一、定义
内存泄漏是指:应该被GC回收的对象无法被回收,这个对象会引发内存泄漏。
二、危害
1、引发内存溢出;
2、导致内存不足,频繁触发GC,因而导致UI卡顿;
三、检测工具
1、MAT(Memory Analyzer Tools)是一个分析Java堆数据的专业工具,用它可以定位内存泄漏的原因。
MAT 使用方法:
内存泄漏 之 MAT工具的使用
2、LeakCanary是一个开源的在debug版本中检测内存泄漏的java库。
LeakCanary使用方法:
LeakCanary——直白的展现Android中的内存泄露
四、常见案例和解决方案
● 单例造成泄漏 单例不仅有泄漏问题,还存在同步等其他问题,使用时要很谨慎。单例不要 保存 Activity 的 context view 实例,尽量使用 Application 的 context;如果必须保 存 context 或 view,则使用 soft-reference weak-reference,使用时需要判空,每 次使用先初始化后使用。
● 非静态匿名内部类 非静态匿名内部类,实例化时会保存一份外部类的实例。如果内部类的实例 被 MessageQueue、单例、系统实例所引用,造成了内存泄漏。 Handler 的泄漏,EventBus 的泄漏都是这个原因,此问题在咱们应用中发生 的最为普遍。开发中 Handler,Listerner,callback 大部分使用匿名内部类。 匿名内部类的使用注意: 1、 Handler 尽量使用 WeakHandler, 2、 Listener、callback 的实现中使用单例要注意,可以采用软引用或弱引用 3、 全局实例(EventBus、其他第三方包),要及时释放。
● 线程(异步任务)造成泄漏 发生在线程使用中,activity 销毁时,线程及线程内的任务没有销毁,导致泄漏。
● 资源未释放 对于使用了 BraodcastReceiver ContentObserver,File,Cursor,Stream,Bitmap 等资源的使用,应该在 Activity 销毁时及时关闭或者注销。
● Context的不正确使用 在Android应用程序中通常可以使用两种Context对象:Activity Context和Application Context。当Activity Context被传递到其他实例中,这可能导致自身被引用而发生泄漏。
解决方案:
● 对于大部分非必须使用Activity Context的情况,应该使用Application Context(因为Application Context会随着应用程序的存在而存在,而不依赖于activity的生命周期);
● 对Context的引用不要超过它本身的生命周期,慎重的对Context使用“static”关键字;
● Context里如果有线程,一定要在onDestroy()里及时停掉;
● 集合中对象没清理 我们通常把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大,可能会导致内存泄漏。如果这个集合是static的话,那情况就更严重了。
○ 解决方案:
○ 在Activity退出之前,将集合里的东西clear,然后置为null,再退出程序;
● 9、构造Adapter时,没有使用缓存的ConvertView
初始时ListView会从Adapter中根据当前的屏幕布局实例化一定数量的View对象,同时ListView会将这些View对象 缓存起来。当向上滚动ListView时,原先位于最上面的List Item的View对象会被回收,然后被用来构造新出现的最下面的List Item。这个构造过程就是由getView()方法完成的,getView()的第二个形参View ConvertView就是被缓存起来的List Item的View对象(初始化时缓存中没有View对象则ConvertView是null)。