目录
- 基本概念
- 差异对比
- 使用场景
基本概念
JDK 1.2 开始,将对象引用分为四种级别,从而使程序更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
-
强引用
Object obj = new Object();
上面的
obj
这类对象就具有强引用,属于不可回收资源,在对象可达时,垃圾回收器绝对不会回收它。内存不足时宁愿抛出OutofMemoryError
错误使程序异常终止,也不会靠回收强引用对象来解决内存不足问题。如果想中断或者回收强引用对象,可以显式的将引用赋值为
null
。 -
软引用
Object obj = new Object(); ReferenceQueue queue = new ReferenceQueue(); SoftReference reference = new SoftReference(obj, queue); //强引用对象滞空,保留软引用 obj = null;
当内存空间不足时,为了避免抛出
OutofMemoryError
,垃圾回收器会回收软引用对象。软引用通过与
ReferenceQueue
联合使用,当软引用所引用的对象被垃圾回收时,Java 虚拟机会把这个软引用加入到ReferenceQueue
队列中。判断哪些软引用对象已经被清理:
SoftReference ref = null; while ((ref = (SoftReference) queue.poll()) != null) { //清除软引用对象 }
-
弱引用
Object obj = new Object(); ReferenceQueue queue = new ReferenceQueue(); WeakReference reference = new WeakReference(obj, queue); //强引用对象滞空,保留软引用 obj = null;
当垃圾回收器扫描到弱引用,不管内存空间是否充足,都会进行回收。
-
虚引用
Object obj = new Object(); ReferenceQueue queue = new ReferenceQueue(); PhantomReference reference = new PhantomReference(obj, queue); //强引用对象滞空,保留软引用 obj = null;
如果一个对象与虚引用关联,跟没有引用与之关联是一样的,在任何时候都可能被垃圾回收器回收。
reference.get()
永远返回null
。基本都是需要跟引用队列关联使用。
差异对比
引用类型 | 回收时机 |
---|---|
强引用 | 不回收 |
软引用 | 内存不足时 |
弱引用 | 垃圾回收 |
虚引用 | Unknow |
使用场景
-
大图加载(软引用)
View view = findViewById(***); Bitmap bitmap = BitmapFactory.decodeResource(getResource(),R.mipmap.ic_launcher); Drawable drawable = new BitmapDrawable(bitmap); SoftReference<Drawable> dsf = new SoftReference<Drawable>(drawable); if(dsf!=null){ view.setImageResource(dsf.get()) }
通过软引用持有加载大图避免图片占用内存过大导致 OOM。
-
Handler 内存泄露(弱引用)
MyHandler myHandler = new MyHandler(this); public static class MyHandler extends Handler{ WeakReference<ThisActivity> ref; MyHandler(ThisActivity activity){ ref = new WeakReference<>(activity); } }
如果使用匿名内部类创建 Handler,在 Handler 处理回调中需要对外部类进行操作,这里外部类为 Activity,Handler 就会持有 Activity,当前任务会包装成一条持有 handler 对象的 Message 进入队列 MessageQueue,当消息未被消费时 Activity 被销毁,此时 Message 中的 Handler 依旧持有 Activity 对象,造成 Activity 内存泄露。
解决方案:
- 静态内部类加弱引用
- 在生命周期结束时及时移除消息
参考:Handler 内存泄露
-
LeakCanary
LeakCanary的原理非常简单。正常情况下一个Activity在执行Destroy之后就要销毁,LeakCanary做的就是在一个Activity 被Destroy之后将它放在一个WeakReference中,然后将这个WeakReference关联到一个ReferenceQueue,查看ReferenceQueue队列是否存在这个Activity的引用,如果不在这个队列中,执行一些GC清洗操作,再次查看。如果仍然不存在则证明该Activity泄漏了,之后Dump出heap信息,并用haha这个开源库去分析泄漏路径。
网友评论