概念
内存泄漏是因为持有对象长期引用,导致对象无法被 GC 回收。
为了避免这种情况,我们可以选择在对象生命周期结束的时候,解除绑定,将引用置为空,或者使用弱引用。
例子
- 单例模式导致内存泄露
传入的context为act,但单例的生命周期是app - 非静态内部类、匿名内部类导致内存泄露(静态变量持有Act,非静态内部类的静态实例 )
它们会隐式的持有外部类的引用,长期持有该引用(比如写个循环)就会导致内存泄漏,使用静态内部类和弱引用来代替它们。(因为静态的内部类不会持有外部类的引用) - Handler导致内存泄漏
Handler通过发送Message与主线程交互,Message发出之后是存储在MessageQueue中的,有些Message也不是马上就被处理的。
在Message中存在一个 target,是Handler的一个引用,如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。
如果Handler是非静态的,(因为持有外部activity的引用)会导致Activity或者Service不会被回收。
解决方法 :
- 静态内部类+弱引用
创建一个静态Handler内部类,然后对Handler持有的对象使用弱引用)
静态内部类+弱引用。相当于activity没关闭的时候,act有强弱两种引用,关闭的时候,只有弱引用。 - 非静态内部类+api clean掉message
- 线程造成的内存泄漏
执行一些长期运行的任务,避免在这些任务中持有 Activity 对象的引用,如果持有了引用的话,我们应该在对象生命周期结束的时候,释放引用。 - 资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
LeakCanary
LeakCanary的内存泄露提示一般会包含三个部分:
第一部分(LeakSingle类的sInstance变量)引用第二部分(LeakSingle类的mContext变量), 导致第三部分(MainActivity类的实例instance)泄露.
Java的GC内存回收机制
当我们向上寻找,一直寻找到GC Root的时候,此对象不会进行回收,例如,一个Activity。那么如果我们向上寻找,直到找到GC Root对象的时候,就说明它是不可以回收的,例如,我定义了一个int a;但是这个数据,我整个页面或者说整个项目都没有用到,则这个对象会被GC掉。
GC的引用点
-
java栈中引用的对象
-
方法静态引用的对象
-
方法常量引用的对象
-
Native中JNI引用的对象
-
Thread——“活着的”线程
内存溢出
瞬间大内存:图片加载内存泄漏和内存溢出区别
-
内存溢出是指程序在申请内存的时候,没有足够的内存可以分配,导致Out Of Memory错误,也就是OOM。
-
内存泄漏:对象都有生命周期的,在生命周期完成之后,就该被垃圾回收和释放,如果得不到及时的释放,就会一直占用内存,造成内存泄漏。随着内存泄漏的堆积,可能导致oom。
网友评论