美文网首页
四大引用区别以及应用场景

四大引用区别以及应用场景

作者: gekson | 来源:发表于2020-02-17 22:27 被阅读0次

目录

  1. 基本概念
  2. 差异对比
  3. 使用场景

基本概念

JDK 1.2 开始,将对象引用分为四种级别,从而使程序更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

  1. 强引用

    Object obj = new Object();
    

    上面的 obj 这类对象就具有强引用,属于不可回收资源,在对象可达时,垃圾回收器绝对不会回收它。内存不足时宁愿抛出 OutofMemoryError 错误使程序异常终止,也不会靠回收强引用对象来解决内存不足问题。

    如果想中断或者回收强引用对象,可以显式的将引用赋值为 null

  2. 软引用

    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) {
        //清除软引用对象
    }
    
  3. 弱引用

    Object obj = new Object();
    ReferenceQueue queue = new ReferenceQueue();
    WeakReference reference = new WeakReference(obj, queue);
    //强引用对象滞空,保留软引用
    obj = null;
    

    当垃圾回收器扫描到弱引用,不管内存空间是否充足,都会进行回收。

  4. 虚引用

    Object obj = new Object();
    ReferenceQueue queue = new ReferenceQueue();
    PhantomReference reference = new PhantomReference(obj, queue);
    //强引用对象滞空,保留软引用
    obj = null;
    

    如果一个对象与虚引用关联,跟没有引用与之关联是一样的,在任何时候都可能被垃圾回收器回收。reference.get() 永远返回 null。基本都是需要跟引用队列关联使用。

差异对比

引用类型 回收时机
强引用 不回收
软引用 内存不足时
弱引用 垃圾回收
虚引用 Unknow

使用场景

  1. 大图加载(软引用)

     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。

  2. 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 内存泄露。

    解决方案:

    1. 静态内部类加弱引用
    2. 在生命周期结束时及时移除消息

    参考:Handler 内存泄露

  3. LeakCanary
    LeakCanary的原理非常简单。正常情况下一个Activity在执行Destroy之后就要销毁,LeakCanary做的就是在一个Activity 被Destroy之后将它放在一个WeakReference中,然后将这个WeakReference关联到一个ReferenceQueue,查看ReferenceQueue队列是否存在这个Activity的引用,如果不在这个队列中,执行一些GC清洗操作,再次查看。如果仍然不存在则证明该Activity泄漏了,之后Dump出heap信息,并用haha这个开源库去分析泄漏路径。

    参考:LeakCanary 源码解析
    BlockCanary 源码解析

相关文章

网友评论

      本文标题:四大引用区别以及应用场景

      本文链接:https://www.haomeiwen.com/subject/ntxwfhtx.html