reference分类,weakReference, softReference

weakReference

4165335-9ee4c5f796cd1d12.png
image.png
4165335-01546c94895e6d3b.png
image.png

java中的4种reference的差别和使用场景(含理论、代码和执行结果)

虚引用

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。由于Object.finalize()方法的不安全性、低效性,常常使用虚引用完成对象回收前的资源释放工作。参考我的另一篇博客:解释为什么finalize是不安全的,不建议使用

————————————————
版权声明:本文为CSDN博主「aitangyong」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/aitangyong/article/details/39453365

/**
 * 当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列.
 * 而此时PhantomReference所指向的对象并没有被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收.
 * 
 * <pre>
 * 不发生GC执行结果是:
 * null
 * null
 * null
 * null
 * 
 * 发生GC执行结果是:
 * null
 * null
 * null
 * java.lang.ref.PhantomReference@87816d
 * </pre>
 * 
 * 虚引用在实现一个对象被回收之前必须做清理操作是很有用的,比finalize()方法更灵活
 */
public static void phantom() throws Exception
{
    Object obj = new Object(); //强引用

    ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
    PhantomReference<Object> phantom = new PhantomReference<Object>(obj,refQueue); // obj对象包裹成虚引用对象。


    System.out.println(phantom.get()); // null

    System.out.println(refQueue.poll());// null
 
    obj = null; // 取消强引用

    System.gc(); // 虚引用对象加入到引用队列。

 
    // 调用phanRef.get()不管在什么情况下会一直返回null
    System.out.println(phantom.get());
 
    // 当GC发现了虚引用,GC会将phanRef插入进我们之前创建时传入的refQueue队列
    // 注意,此时phanRef所引用的obj对象,并没有被GC回收,在我们显式地调用refQueue.poll返回phanRef之后
    // 当GC第二次发现虚引用,而此时JVM将phanRef插入到refQueue会插入失败,此时GC才会对obj进行回收
    Thread.sleep(200);
    System.out.println(refQueue.poll());
}

Reference

  • A Reference instance is in one of four possible internal states:
    引用实例处于四种可能的内部状态之一:

Active: Some time after the collector detects that the reachability of the referent has changed to the appropriate state, it changes the instance's state to either Pending or Inactive, depending upon whether or not the instance was registered with a queue when it was created. In the former case it also adds the instance to the pending-Reference list. Newly-created instances are Active.
活跃状态

Pending: An element of the pending-Reference list, waiting to be enqueued by the Reference-handler thread. Unregistered instances are never in this state.
等待加入队列

Enqueued: An element of the queue with which the instance was registered when it was created. When an instance is removed from its ReferenceQueue, it is made Inactive. Unregistered instances are never in this state.
已经加入到队列

Inactive: Nothing more to do. Once an instance becomes Inactive its state will never change again.
最后的状态

  • Reference对象的字段queue、 字段next各自对应值的组合, 对应了Reference对象的状态。

Active: queue = ReferenceQueue with which instance is registered, or ReferenceQueue.NULL if it was not registered with a queue; next = null.
当Reference对象的queue = ReferenceQueue 或者ReferenceQueue.NULL
next=null
表明 Reference对象在Active状态。

Pending: queue = ReferenceQueue with which instance is registered; next = this
当Reference对象的queue = ReferenceQueue
next=this
表明 Reference对象在Pending状态。就是等待被添加到队列中的状态。

Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance in queue, or this if at end of list.
当Reference对象的queue = ReferenceQueue.ENQUEUED;
next= Following instance in queue 或者 this
表明 Reference对象在Enqueued状态。表明已经被添加到了队列中。

Inactive: queue = ReferenceQueue.NULL; next = this.
这个表示把reference从队列中给删除了。

   // 被包装的对象。
    private T referent;         /* Treated specially by GC */

   // 队列。
    volatile ReferenceQueue<? super T> queue;

    /* When active:   NULL
     *     pending:   this
     *    Enqueued:   next reference in queue (or this if last)
     *    Inactive:   this
     */
    @SuppressWarnings("rawtypes")
    Reference next; // 结合ReferenceQueue. enqueue方法来看。


/* When active:   next element in a discovered reference list maintained by GC (or this if last)
     *     pending:   next element in the pending list (or null if last)
     *   otherwise:   NULL
     */
// 虚拟机直接控制
    transient private Reference<T> discovered;  /* used by VM */



    /* List of References waiting to be enqueued.  The collector adds
     * References to this list, while the Reference-handler thread removes
     * them.  This list is protected by the above lock object. The
     * list uses the discovered field to link its elements.
     */
//垃圾回收器把reference对象添加到这个list, 而Reference-handler thread 将删除它们。这个list通过discovered field to link its elements.
    private static Reference<Object> pending = null;



 static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    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;
                    r.discovered = null;
                } else {
                    // The waiting on the lock may cause an OutOfMemoryError
                    // because it may try to allocate exception objects.
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // retry if waited
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // Give other threads CPU time so they hopefully drop some live references
            // and GC reclaims some space.
            // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above
            // persistently throws OOME for some time...
            Thread.yield();
            // retry
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }

        // Fast path for cleaners
        if (c != null) {
            c.clean();
            return true;
        }

        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }

static {
        ThreadGroup tg = Thread.currentThread().getThreadGroup();
        for (ThreadGroup tgn = tg;
             tgn != null;
             tg = tgn, tgn = tg.getParent());
        Thread handler = new ReferenceHandler(tg, "Reference Handler");
        /* If there were a special system-only priority greater than
         * MAX_PRIORITY, it would be used here
         */
        handler.setPriority(Thread.MAX_PRIORITY);
        handler.setDaemon(true);
        handler.start();

        // provide access in SharedSecrets
        SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() {
            @Override
            public boolean tryHandlePendingReference() {
                return tryHandlePending(false);
            }
        });
    }

ReferenceQueue

 boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */
        synchronized (lock) {
            // Check that since getting the lock this reference hasn't already been
            // enqueued (and even then removed)
            ReferenceQueue<?> queue = r.queue; // reference实例的注册的队列


            if ((queue == NULL) || (queue == ENQUEUED)) {
                return false;
            }
            assert queue == this;


            r.queue = ENQUEUED;// reference对象已经入队了。标记。


               // 插入第一个节点 head ---> head
             // 插入第二个节点 head ---> next
            r.next = (head == null) ? r : head; // Reference.next字段
            head = r;

            queueLength++;


            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }


            lock.notifyAll();
            return true;
        }
    }

删除节点


 @SuppressWarnings("unchecked")
    private Reference<? extends T> reallyPoll() {       /* Must hold lock */
        Reference<? extends T> r = head;
        if (r != null) {
            head = (r.next == r) ?
                null :
                r.next; // Unchecked due to the next field having a raw type in Reference
            r.queue = NULL; // reference对象被移除出队列,标识。



            r.next = r;
            queueLength--;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(-1);
            }
            return r;
        }
        return null;
    }


  // 有就删除,没有就算
    public Reference<? extends T> poll() {
        if (head == null)
            return null;
        synchronized (lock) {
            return reallyPoll();
        }
    }

 // 有就删除, 没有的话, 等待新节点的插入, 然后删除新节点。
    public Reference<? extends T> remove(long timeout)
        throws IllegalArgumentException, InterruptedException
    {
        if (timeout < 0) {
            throw new IllegalArgumentException("Negative timeout value");
        }
        synchronized (lock) {
            Reference<? extends T> r = reallyPoll();
            if (r != null) return r;
            long start = (timeout == 0) ? 0 : System.nanoTime();
            for (;;) {
                lock.wait(timeout); // 等待新节点的插入

                r = reallyPoll();
                if (r != null) return r;
                if (timeout != 0) {
                    long end = System.nanoTime();
                    timeout -= (end - start) / 1000_000;
                    if (timeout <= 0) return null;
                    start = end;
                }
            }
        }
    }

reference对象、referenceQueue对象、 被包装的对象referent、 垃圾回收器的关系

SoftReference

  • weakReference + referent

当被包装的对象清除了对它的强引用,则在下次gc的时候,只有当内存不足的时候才会把referent对象回收。

  • weakReference + referenceQueue + referent
    当被包装的对象清除了对它的强引用,

则在下次gc的时候,就会把reference对象添加到referenceQueue,

当reference对象被移除出referenceQueue,只有当内存不足的时候才会把referent对象回收。

weakReference

  • weakReference + referent

当被包装的对象清除了对它的强引用,则在下次gc的时候,就会把referent对象回收。

  • weakReference + referenceQueue + referent
    当被包装的对象清除了对它的强引用,

则在下次gc的时候,就会把reference对象添加到referenceQueue,

当reference对象被移除出referenceQueue,下次gc的时候,就会把referent对象回收。

PhantomReference

  • PhantomReference必须添加一个referenceQueue对象;
    因为只有一个构造方法:
 public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }
  • PhantomReference + referenceQueue + referent
    当被包装的对象清除了对它的强引用,

则在下次gc的时候,就会把reference对象添加到referenceQueue,

当reference对象被移除出referenceQueue,下次gc的时候,就会把referent对象回收。

  • 不能获取被包装的对象
    public T get() {
        return null;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Java 中,对象的生命周期由垃圾回收器(GC)管理。为了让开发人员更好地控制对象的生命周期,Java 提供了三种不同类型的引用:强引用(Strong Reference)、软引用(Soft Reference)和弱引用(Weak Reference)。它们之间的主要区别在于垃圾回收器对它们进行回收的时机和方式。 1. 强引用(Strong Reference) 强引用是最普通的引用类型,我们平常使用的引用默认都是强引用。如果一个对象具有强引用,那么垃圾回收器就不会回收这个对象,即使内存不足时也不会回收。当内存空间不足时,JVM 宁可抛出 OutOfMemoryError 错误,也不会回收具有强引用的对象。 2. 软引用(Soft Reference) 软引用是一种相对强引用弱化了一些的引用,需要用 SoftReference 类来实现。如果一个对象只有软引用,那么当内存空间不足时,垃圾回收器就可能会回收这个对象。软引用通常用于实现内存敏感的缓存,即缓存中的对象如果没有被使用,就可以被回收。 3. 弱引用(Weak Reference) 弱引用是一种比软引用更弱化了一些的引用,需要用 WeakReference 类来实现。如果一个对象只有弱引用,那么当垃圾回收器进行扫描时,无论当前内存空间是否充足,都会回收这个对象。 弱引用通常用于实现一些缓存功能,如 ThreadLocal 中的缓存。另外,弱引用还可以用来解决内存泄漏问题,如监听器和回调中的对象引用,如果使用强引用,容易造成内存泄漏,使用弱引用可以及时释放不需要的对象。 总之,强引用是最常见的引用类型,能够保证对象的存活,但也容易造成内存泄漏;软引用适用于内存敏感的缓存场景,可以根据内存情况动态调整缓存大小;弱引用用于不需要强制保持对象存活的场景,如缓存、监听器等,可以有效防止内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值