定义
当一个对象已经不需要再使用了,本应该被回收掉,而另一个正在使用的对象持有他的引用,导致对象不能被回收。因为不能被及时回收的本该被回收的内存,就产生了内存泄露。如果内存泄露太多会导致程序没有办法申请内存,最后出现内存溢出的错误。
场景和解决办法
Android开发中经常出现内存泄露的点,
- 使用单例模式
- 非静态内部类的静态实例
非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,
就会长期维持着外部类的引用,组织被系统回收,解决办法就是使用静态内部类。
- 多线程相关的匿名内部了和非静态内部类
匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发生内存泄露
导致外部类无法回收,直到耗时任务结束,解决办法就是在页面退出时结束线程中的任务。
-
Handler内存泄漏
Handler导致的内存泄露也可以被归纳为非静态内部类导致的,
handler内部message是被存储在MessageQueue中的,
有些message不能马上被处理,存在的时间会很长,导致handler无法被回收,
如果handler是非静态的,就会导致他的外部类无法被回收,
解决办法就是
1.使用静态handler,外部类引用使用弱引用处理。
2.在退出页面时移除消息队列中的消息。
-
Context导致内存泄露
根据场景使用Activity的Context还是Application的Context,
因为二者的生命周期不同,对于不必须使用Activity的Context的场景(dialog),
一律采用Application的Context,单例模式是最常见的发生此泄露的场景,
比如传入一个Activity的Context被静态类引用,导致无法回收。
- 静态
View导致泄露
使用静态View可以避免每次启动Activity都去读取并渲染View,
但静态View会持有Activity的引用,导致无法回收,
解决办法就是
在Activity销毁的时候将静态View设置为null(View一旦被加载到界面中将会持有一个Context对象的引用)
-
WebView导致的内存泄露
WebView 只要使用一次,内存就不会被释放,所以WebView都存在内存泄露的问题,
通常解决办法是为WebView单开一个进程,使用AIDL就行通信,根据业务需求在合适的时机释放。
- 使用静态变量
- 资源未关闭导致
如Cursor,File等,内部往往都使用了缓存,会造成内存泄露,一定要确保关闭它并将引用置为null
- 集合中的对象未清理
集合用于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的
- 监听器未关闭
很多需要register和unregister的系统服务要在合适的时候进行unregister,
手动添加的listener也需要及时移除。
- 使用
Asynctask - 使用
Bitmap
bitmap是比较占内存的,所以一定要在不使用的时候及时进行清理,
避免静态变量持有大的bitmap对象
优化
- 当查询完数据库之后,及时关闭
Cursor对象。 - 记得在
Activity的onPause方法中调用unregisterReceiver()方法,反注册广播。 - 避免
Context内存泄露,比如在4.0.1之前的版本上不要将Drawer对象置为static。当一个Drawable绑定到了View上,实际上这个View对象就会成为这个Drawable的一个CallBack成员变量,静态的SBackground持有TextView对象lable的引用,而lable只有Activity的引用,而Activity会持有其他更多对象的引用,sBackground生命周期要长于Activity,当屏幕旋转时,Activity无法销毁,这样就产生了内存泄露的问题。 - 尽量不要在
Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,当非静态内部类的引用的生命周期长于Activity的生命周期时,会导致Activity无法被GC正常回收掉。 - 谨慎使用线程
Thread,很多人会犯这样的错误:Java中的Thread有一个特点就是他们都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用 ,导致GC永远都无法回收掉这些线程对象,除非线程被手动停止并置为null或者用户直接kill掉进程操作,所以当使用线程时,一定要考虑在Activity退出时,及时将线程也停止并释放掉。 - 使用
Handler时,要么放在单独的类文件中, 要么就是使用静态内部类,因为静态的内部类不会持有外部类的引用,所以不会导致外部类实例的内存泄露
参考资料:
Android性能优化篇之内存优化--内存泄漏
资料













网友评论