检测内存泄漏的工具有LeakCanary、MAT等工具。
一、内存泄漏的原因:
当一个对象已经不需要使用,本该被回收,而另一个正在使用的对象持有它的引用,导致不能被回收,而停留在堆内存中,产生了内存泄漏。通常是Activity或者Fragment的泄露。
二、内存泄漏对程序的影响:
内存泄漏是造成应用程序OOM的主要原因之一。系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时,难免会导致应用所需要的内存超过系统分配的内存限额,造成内存溢出而导致crash。
三、常见的内存泄漏:
1、单例造成的内存泄漏
由于单例的静态特性使得单例的生命周期和应用的生命周期一样长,如果一个对象已经不使用,而单例对象还持有该对象的引用,导致不能正常被回收,发生内存泄漏。
由于需要传入Context,这个Context的生命周期长短至关重要。
1)、传入的是Application的Context,这将没有任何问题,因为单例的生命周期和Application的一样长。
2)、传入的是Activity的Context。当这个Context所对应的Activity退出时,由于该Context和Activity的生命周期一样长,当前的Activity退出时它的内存并不会被回收,因为单例对象持有该Activity的引用。
正确的单例应用:
不管传入什么Context最终使用的Application的Context。
2、非静态内部类创建静态实例造成的内存泄漏。
非静态内部类默认会持有外部类的引用,而又使用该非静态内部类创建了一个静态的实例,该实例的生命周期和应用一样长,导致该静态实例会一直持有该Activity的引用,导致Activity的内存资源不能正常回收。
正确的做法是:将该内部类设为静态内部类或将该内部类变成一个单例。
3、Handler造成的内存泄漏。
由于mHandler是Handler的非静态匿名内部类的实例。所以它持有外部类Activity的引用。消息队列是在一个Looper线程中不断轮询处理消息,当这个Activity退出时消息队列中还有未处理的消息或者正在处理的消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,导致该Activity的内存资源无法及时回收。
正确的做法是:
创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用,这样在回收时也可以回收Handler持有的对象。在Activity的Destroy时或者Stop时应该移除消息队列中的消息。使用mHandler.removeCallbacksAndMessages(null);是移除消息队列中所有消息和所有的Runnable。当然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();来移除指定的Runnable和Message。
4、线程造成的内存泄漏。
上诉都是匿名内部类,对当前Activity都有一个隐式引用。如果在Activity销毁之前,任务还未完成,那么将导致Activity的内存资源无法回收,造成内存泄漏。
正确做法还是使用静态内部类的方式。
5、资源未关闭造成的内存泄漏。
对于使用BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等,应该在Activity销毁时及时关闭或者注销,否则这些资源不会被回收,造成内存泄漏。
6、静态的View
有时,当一个Activity经常启动,但是对应的View读取非常耗时,我们可以通过静态View变量来保持对该Activity的rootView引用。这样就可以不用每次启动Activity都去读取并渲染View了。这确实是一个提高Activity启动速度的好方法!但是要注意,一旦View attach到我们的Window上,就会持有一个Context(即Activity)的引用。而我们的View有事一个静态变量,所以导致Activity不被回收。
解决办法:
在使用静态View时,需要确保在资源回收时,将静态View detach掉。
7、监听器(各种需要注册的Listener,Watcher等)
例如:EditText的一个addTextChangeListener,如果在回调方法里有耗时操作,可能会造成内存泄露。
解决办法:
在onDestory时,取消注册,editText.removeTextChangedListener。
8、属性动画
在使用ValueAnimator或者ObjectAnimator,如果没有及时做cancel取消动画,就可能造成内存泄漏。
解决办法:在onDestory()调用动画的cancel()方法。cancel()方法最后调用了endAnimation()。
9、RxJava
在使用RxJava,如果发布一个订阅后,由于没有及时取消,导致Activity/Fragment无法销毁,导致内存泄漏。
解决方法:Android架构中添加AutoDispose解决RxJava内存泄漏
https://blog.csdn.net/mq2553299/article/details/79418068
10、WebView
在Android5.1及以上版本,webView可能存在内存泄漏。
解决办法:在销毁webview前一定要onDetachedFromWindow。先将webview从它的父view中移除再调用onDestory方法
Android5.1WebView内存泄漏及解决:https://blog.csdn.net/u013085697/article/details/53259116
11、其他系统控件以及自定义View
在 Android Lollipop 之前使用 AlertDialog 可能会导致内存泄漏:
https://blog.csdn.net/u012464435/article/details/50774580
Dialog和DialogFragment在Android5.0以下的内存泄漏:
https://www.cnblogs.com/endure/p/7664320.html
View的post方法导致的内存泄漏:
http://www.mamicode.com/info-detail-1753936.html