内存泄漏(Memory Leak)
当一个对象已经不需要使用了,本该被回收时,
而有另外一个正在使用的对象持有它的引用,从而导致了对象不能被GC回收。
内存溢出(OOM
- Out Of Memory)
当应用程序的堆(heap)资源超过了 Dalvik
虚拟机分配的内存就会内存溢出。
注:内存泄漏与内存溢出的区别与联系!
内存泄漏带来的影响:
1.
影响内存分配,造成应用程序执行效率降低;
2.
可分配的内存越少,更加容易出现内存溢出;
--------------------------------------------------------------------------------
成员变量全部存储在堆中(包括基本数据类型,引用及引用的对象实体),
因为他们属于类,类对象最终还是要被new出来的。
局部变量的基本数据类型和引用存在栈中,应用的对象实体存储在堆中。
因为它们属于方法当中的变量,生命周期会随着方法一起结束。
1.
类型
StrongReference 强引用
回收时机
任何时候GC是不能回收他的,哪怕内存不足时,
系统会直接抛出异常OutOfMemoryError,也不会去回收
生命周期
进程终止
类型
SoftReference 软引用
回收时机
当内存足够时不会回收这种引用类型的对象,只有当内存不够用时才会回收
生命周期
内存不足,进行GC的时候
2.
类型
WeakReference 弱引用
回收时机
GC一运行就会把它回收了
生命周期
GC后终止
3.
类型
PhantomReference 虚引用
回收时机
如果一个对象与虚引用关联,则跟没有引用与之关联一样,
在任何时候都可能被垃圾回收器回收
生命周期
任何时候都有可能
开发时,为了防止内存溢出,处理一些比较占用内存并且生命周期长的对象时,
可以尽量使用软引用和弱引用。
--------------------------------------------------------------------------------
1.资源未关闭造成的内存泄漏:
对于使用了
BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap
等资源的使用,
应该在Activity销毁时及时关闭或者注销,
否则这些资源将不会被回收,造成内存泄漏。
解决方案:
在Activity销毁时及时关闭或者注销。
2.单例造成的内存泄漏:
当调用getInstance时,如果传入的context 是 Activity 的 context 。
只要这个单例没有被释放,那么这个 Activity也不会被释放一直到进程退出才会释放。
解决方案:
能使用 Application 的 Context就不要使用 Activity 的 Content,
Application的生命周期伴随着整个进程的周期。
3.使用了静态的
Activity 和 View :
解决方案:
应该及时将无用的静态对象/变量置为null,
而且一般不建议将 View 及 Activity设置为静态。
4.线程造成的内存泄漏:
异步任务和 Runnable都是一个匿名内部类,
因此它们对当前 Activity都有一个隐式引用。
如果 Activity在销毁之前,任务还未完成,
那么将导致 Activity的内存资源无法回收,造成内存泄漏。
解决方案:
使用静态内部类,避免了 Activity的内存资源泄漏,
当然在 Activity销毁时候也应该取消相应的任务,
避免任务在后台执行浪费资源。
5.注册了系统的服务,但 onDestory 未注销:
解决方案:
不需要用的时候记得移除监听。
6.不需要用的监听未移除会发生内存泄露:
解决方案:
监听执行完回收对象,不用考虑内存泄漏
7.非静态内部类创建静态实例造成的内存泄漏:
解决方案:
将非静态内部类修改为静态内部类。
静态内部类不会隐式持有外部类。
8.Handler
造成的内存泄漏:
mHandler 是 Handler的非静态匿名内部类的实例,所以它持有外部类 Activity 的引用,
消息队列是在一个 Looper线程中不断轮询处理消息,
那么当这个 Activity退出时消息队列中还有未处理的消息或者正在处理消息,
而消息队列中的 Message 持有mHandler 实例的引用,
mHandler 又持有 Activity的引用,
所以导致该 Activity的内存资源无法及时回收,引发内存泄漏。
解决方案:
创建一个静态 Handler 内部类,然后对Handler 持有的对象使用弱引用,
这样在回收时也可以回收 Handler持有的对象,这样虽然避免了 Activity 泄漏,
不过 Looper线程的消息队列中还是可能会有待处理的消息,
所以在 Activity 的 onDestroy时或者 onStop 时应该移除消息队列中的消息。