内存泄漏总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存泄漏总结相关的知识,希望对你有一定的参考价值。
内存泄漏主要有几大情景:
1,activity泄漏
2,静态常量泄漏 静态变量长期维持到大数据对象的引用,阻止垃圾回收
3,资源未关闭泄漏 资源性对象如Cursor、Stream、Socket,Bitmap
4,注册反注册泄漏 我们常常写很多的Listener,未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。
最主要的最普遍的是activity泄漏 主要情景: (在 activity关闭时,检查静态变量是否需要赋值为null,检查内部类是否还存在)
1,非静态内部类的静态实例
由于内部类默认持有外部类的引用,而静态实例属于类。所以,当外部类被销毁时,内部类仍然持有外部类的引用,致使外部类无法被GC回收。因此造成内存泄露
解决方案:将内部类声明为静态内部类,或者在创建静态内部类的时候使用静态变量名引用,并在activity关闭时将静态变量赋值为null
2,Handler临时性内存泄露
Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。
解决方案:可以由上面的结论看出,产生泄漏的根源在于匿名类持有Activity的引用,因此可以自定义Handler和Runnable类并声明成静态的内部类,来解除和Activity的引用。或者在activity 结束时,将发送的Message移除
3.内部线程泄露
new Thread(){}.start();Java中的Thread有一个特点就是她们都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用,导致GC永远都无法回收掉这些线程对象,除非线程被手动停止并置为null或者用户直接kill进程操作。看到这相信你应该也是心中有答案了吧 : 我在每一个MainActivity中都创建了一个线程,此线程会持有MainActivity的引用,即使退出Activity当前线程因为是直接被GC Root引用所以不会被回收掉,导致MainActivity也无法被GC回收。
解决方案:当使用线程时,一定要考虑在Activity退出时,及时将线程也停止并释放掉
4.单例(主要原因还是因为一般情况下单例都是全局的,有时候会引用一些实际生命周期比较短的变量,导致其无法释放)
有的单例模式,在创建的时候需要传递上下文,这是把当前activity对象传入,创建了静态单例。从此静态单例持有activity对象,造成activity泄漏
解决方案: 传递activity的弱饮用或者软引用,或者传递上下文时传递生命周期为全程的上下文。比如application上下文,全程都存在的activity
5.静态Activities(static Activities)
activity有个静态activity成员变量,然后在生命周期中将自己赋值给这个静态常量,这样导致被持有的activity泄漏
解决方案:不使用静态activity,或给静态activity赋值时,考虑赋值的activity生命周期是不是全局的,或者在静态activity使用完后及时释放
6.静态View
在Activity里声明一个静态变量view,然后初始化。问题出在这里,View一旦被加载到界面中将会持有一个Context对象的引用,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity,所以当activity生命周期结束了,静态View没有清除掉,还持有activity的引用,因此内存泄漏了。
解决方案:不使用静态view,或在activity关闭时将静态view赋值为null
7,匿名类
匿名类和非静态内部类相同,会持有外部类对象,也就是activity,因此如果你在Activity里创建一个匿名内部类,则可能会发生内存泄漏,如果这个内部类在Activity销毁后还一直存在,就会造成activity泄漏。
解决方案:经匿名内部类,改为静态内部类。或者用静态变量名引用匿名内部类,并在activity结束时将静态内部类变量赋值null
8. Timer Tasks
这里内存泄漏在于Timer和TimerTask没有进行Cancel,从而导致Timer和TimerTask一直引用外部类Activity。根本原因就是内部类的使用造成持有外部类activity的引用
解决方案:在适当的时机进行Cancel。
9.注册没有反注册
通过Context调用getSystemService获取系统服务,这些服务运行在他们自己的进程执行一系列后台工作或者提供和硬件交互的接口,如果Context对象需要在一个Service内部事件发生时随时收到通知,则需要把自己作为一个监听器注册进去,这样服务就会持有一个Activity,如果开发者忘记了在Activity被销毁前注销这个监听器,这样就导致内存泄漏。
解决方案:在onDestroy方法里注销监听器。
内存监测工具
1. android Studio自带工具Memory Monitor
2.MAT
3.LeakCanary
以上是关于内存泄漏总结的主要内容,如果未能解决你的问题,请参考以下文章