前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中弱引用、软引用、虚引用、强引用、 Finalizer引用

Java中弱引用、软引用、虚引用、强引用、 Finalizer引用

作者头像
良辰美景TT
发布2018-12-24 11:03:44
2K0
发布2018-12-24 11:03:44
举报

在Java层面,一共有四种引用:强引用、软引用、弱引用、虚引用,这几种引用的生命周期由强到弱。转换关系大致如下图所示:

强引用(Strong Reference)

  就是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为 null,就是可以被垃圾收集的了。

软引用(Soft Reference)

   实现类为:SoftReference。只有当JVM认为内存不足时,才会试图回收软引用指向的对象,JVM会确保在抛出OutOfMemoryError之前,清理软引用指向的对象。(适合做缓存)通过下面的代码可以验证:

代码语言:javascript
复制
import java.lang.ref.SoftReference;
public class SoftReferenceTest {
    //-Xms25m -Xmx25m -Xmn20m -XX:+PrintGCDetails 
    public static void main(String[] args) {
        softReference();
    }
    

    public static void softReference() {
        //申请10M的数据
        byte[] referent = new byte[1024*1024*10];
        SoftReference<Object> softRerference = new SoftReference<Object>(referent);
        referent = null;
        //不会回收软引用的数据,
        System.gc();
        //软引用的对象在内存充足的情况下不会回收
        if(softRerference.get() != null){
            System.out.println("true");
        }else{
            System.out.println("false");
        }
        //因为空间不足,会回收软引用的数据
        byte[] another = new byte[1024*1024*10];
        if(softRerference.get() != null){
            System.out.println("true");
        }else{
            System.out.println("false");
        }
        System.out.println("end");
    }
}
弱引用(Weak Reference):

   实现类为:WeakReference。可以用来构建一种没有特定约束的关系,同样是缓存实现的选择(WeekHashMap就是采用弱引用的方式实现的)。JVM一旦发现了某个对象只有弱引用与之关联,不管当前内存空间足够与否,都会回收它的内存。下面代码可以验证:

代码语言:javascript
复制
import java.lang.ref.WeakReference;

public class WeakReferenceTest {
    
    // -Xms25m -Xmx25m -Xmn20m -XX:+PrintGCDetails
    public static void main(String[] args) {
        weekReference();
    }

    public static void weekReference() {
        // 申请10M的数据
        byte[] referent = new byte[1024 * 1024 * 10];
        WeakReference<Object> softRerference = new WeakReference<Object>(referent);
        referent = null;
        //弱引用的数据,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
        System.gc();
        // 软引用的对象在内存充足的情况下不会回收
        if (softRerference.get() != null) {
            System.out.println("true");
        } else {
            System.out.println("false");
        }
    }
}
幻象引用(Phantom Reference)

  实现类为:PhantomReference。提供了一种确保对象被finalize以后,做某些事情的机制。(Java平台自身的Cleaner机制)如:申请堆外内存时,在JVM堆中会创建一个对应的Cleaner对象,这个Cleaner类继承了PhantomReference,当DirectByteBuffer对象被回收时,可以执行对应的Cleaner对象的clean方法,做一些后续工作,这里是释放之前申请的堆外内存。

引用何时被加到ReferenceQueue队列里

  在构造软引用,弱引用和幻象引用的时候,可以传入一个ReferenceQueue的对象,这个队列是用来做什么的呢?当软引用,弱引用和幻象引用所引用的对象被回收之后,对应的SoftReference,WeakReference,PhantomReference 对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量Reference对象带来的内存泄漏。而这个队列就是由JVM将引用对象加入到队列里,由JVM将Reference对象清理。加入队列是由ReferenceHandler这个线程来来做的,代码如下图所示:

  • tryHandlePending方法的代码如下:
代码语言:javascript
复制
    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) { //pending由JVM进行赋值
                    r = pending;
                    // 'instanceof' might throw OutOfMemoryError sometimes
                    // so do this before un-linking 'r' from the 'pending' chain...
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // unlink 'r' from 'pending' chain
                    pending = r.discovered; //将pending的值往下移
                    r.discovered = null;
                } else {
                    if (waitForNotify) {
                        lock.wait();
                    }
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            Thread.yield();
            // retry
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }
//Cleaner 类型的直接掉用clean对象,不会加入到队列里了
        if (c != null) {
            c.clean();
            return true;
        }
//这里将Reference对象加入到队列里
        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }
Finalizer引用

  Finalizer继承Reference,Finalizer在我们的系统里无法被构造(类被定义成package final 类型),Finalizer的实例是一个双向链表的结构,内部有prev与next指针,提供了add与remove方法将对象增加到链表与从链表中删除对象。任何类只要实现了Object类里的finalize方法,JVM在初使化这个对象的时候(调用构造方法的时候),会构造一个Finalizer对象,通过调用Finalizer的register方法,代码如下:

在构造方法里,会调用add方法,将Finalizer对象加入到链表里,代码如下:

,我们分析dump内存的时候,经常能看到 java.lang.ref.Finalizer占用的内存大小远远排在前面,就是因为系统里构造了大量的实现了finalize方法的对象。

何时被加入到ReferenceQueue里

  当gc发生的时候,gc算法会判断对象是不是只被Finalizer类引用,如果这个类仅仅被Finalizer对象引用的时候,说明这个对象在不久的将来会被回收了现在可以执行它的finalize方法了,于是会将这个Finalizer对象放到Finalizer类的ReferenceQueue里,但是这个f类对象其实并没有被回收,因为Finalizer这个类还对他们持有引用,在gc完成之前,jvm会调用ReferenceQueue里的lock对象的notify方法(当ReferenceQueue为空的时候,FinalizerThread线程会调用ReferenceQueue的lock对象的wait方法直到被jvm唤醒)

何时调用finalize方法

  Finalizer类里定义了FinalizerThread,用于将ReferenceQueue里的对象取出并执行finalize方法。具体代码如下:

软引用的具体回收时机可以参考:https://www.jianshu.com/p/e46158238a77 参考文章:https://time.geekbang.org/column/article/6970 https://www.jianshu.com/p/e46158238a77 https://www.jianshu.com/p/7200da8b043f https://mp.weixin.qq.com/s/fftHK8gZXHCXWpHxhPQpBg

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.12.07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 强引用(Strong Reference)
  • 软引用(Soft Reference)
  • 弱引用(Weak Reference):
  • 幻象引用(Phantom Reference)
  • 引用何时被加到ReferenceQueue队列里
  • Finalizer引用
    • 何时被加入到ReferenceQueue里
      • 何时调用finalize方法
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档