@(Java)[Reference]
ReferenceObject encapsulates references to other objects and can be operated like ordinary objects. Under certain restrictions, interaction with the garbage collector is supported. That is, you can use the Reference object to refer to other objects, but it will still be recycled by the garbage collector in the end. Programs sometimes need to be notified after an object is recycled to inform it of changes in the object's reachability.
Java provides four different types of references. The reference levels from high to low are FinalReference, SoftReference, WeakReference, and PhantomReference. Among them, FinalReference is not available for external use. Each type corresponds to a different level of accessibility.
Strong reference means that there is a directly reachable reference in the program without passing any reference object , such as Object obj = new Object();, obj is a strong reference.
Soft reference, not a strong reference, but can be accessed through a soft reference object. For soft reference objects, the garbage collector will decide to recycle the object pointed to by the soft reference only when there is insufficient memory (before an OOM exception is thrown). Soft references are often used to implement memory-sensitive caches.
SoftReference<Object> softRef = new SoftReference<Object>(new Object());
Weak reference, non-strong reference and soft reference, but can be accessed through a weak reference object. A weakly referenced object will be recycled as long as it is discovered by the garbage collector, regardless of whether the memory is sufficient. For actual applications, see WeakHashMap, etc.
WeakReference<Object> weakRef = new WeakReference<Object>(new Object());
Virtual reference, this reference must be used together with the reference queue (ReferenceQueue), and is generally used to track the recycling actions of the garbage collector, such as when the object is recycled , the finalize method of the object will be called. This action can be achieved using virtual references, which is also safer.
Object obj = new Object(); ReferenceQueue<Object> refQueue = new ReferenceQueue<>(); PhantomReference<Object> phantom = new PhantomReference<Object>(obj, refQueue);
As a member of the reference, this queue can be used in combination with the above three reference types. The function of this queue is: when creating a Reference, register the Queue into the Reference. When the object referenced by the Reference is recycled by the garbage collector, the Reference will be placed in the queue, which is equivalent to a notification mechanism.
Example Demo1:
ReferenceQueue queue = new ReferenceQueue(); WeakReference reference = new WeakReference(new Object(), queue); System.out.println(reference); System.gc(); Reference reference1 = queue.remove(); System.out.println(reference1);
There are several important attributes inside Reference
// 用于保存对象的引用,GC会根据不同Reference来特别对待 private T referent; // 如果需要通知机制,则保存的对对应的队列 ReferenceQueue<? super T> queue; /* 这个用于实现一个单向循环链表,用以将保存需要由ReferenceHandler处理的引用 */ Reference next; static private class Lock { }; // 锁,用于同步pending队列的进队和出队 private static Lock lock = new Lock(); // 此属性保存一个PENDING的队列,配合上述next一起使用 private static Reference pending = null;
ReferenceHandler is a static internal class of Reference, which is used to add Reference instances in the pending queue to different in the ReferenceQueue (depends on the queue in the Reference). The pending elements are added by the GC.
Note: The pending queue is locked here. Personally, I think it is because the GC thread may execute concurrently with the thread where the ReferenceHandler is located, such as when GC uses CMS concurrent collection.
As shown in the following code
// 此线程在静态块中启动,即一旦使用了Reference,则会启动该线程 private static class ReferenceHandler extends Thread { public void run() { for (;;) { Reference r; synchronized (lock) { if (pending != null) { r = pending; Reference rn = r.next; // 从pending中取下一个元素,如果后继为空,则next指向自身 pending = (rn == r) ? null : rn; r.next = r; } else { try { // 没有则等待,后续加入元素会调用lock.notify唤醒 lock.wait(); } catch (InterruptedException x) { } continue; } } // ... ReferenceQueue q = r.queue; // 如果该Reference注册了对应的Queue,则加入到该Queue中 if (q != ReferenceQueue.NULL) q.enqueue(r); } } }
// 用于标识没有注册Queue static ReferenceQueue NULL = new Null(); // 用于标识已经处于对应的Queue中 static ReferenceQueue ENQUEUED = new Null(); static private class Lock { }; /* 互斥锁,用于同步ReferenceHandler的enqueue和用户线程操作的remove和poll出队操作 */ private Lock lock = new Lock(); // 队列 private volatile Reference<? extends T> head = null; // 队列中的元素个数 private long queueLength = 0;
This method will only be called through the Reference, which is used to put the Reference into the current In the queue
boolean enqueue(Reference<? extends T> r) { synchronized (r) { // 判断是否已经入队了 if (r.queue == ENQUEUED) return false; synchronized (lock) { r.queue = ENQUEUED; // 单向循环 r.next = (head == null) ? r : head; head = r; queueLength++; if (r instanceof FinalReference) { sun.misc.VM.addFinalRefCount(1); } // 通知当前挂起的线程(调用remove时有可能会挂起) lock.notifyAll(); return true; } } }
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; for (;;) { // 否则等待,由enqueue时notify唤醒 lock.wait(timeout); r = reallyPoll(); if (r != null) return r; if (timeout != 0) return null; } } }
Using the above example Demo1 as an analysis
// 创建一个引用队列 ReferenceQueue queue = new ReferenceQueue(); // 创建虚引用,此时状态为Active,并且Reference.pending为空,当前Reference.queue = 上面创建的queue,并且next=null WeakReference reference = new WeakReference(new Object(), queue); System.out.println(reference); // 当GC执行后,由于是虚引用,所以回收该object对象,并且置于pending上,此时reference的状态为PENDING System.gc(); /* ReferenceHandler从pending中取下该元素,并且将该元素放入到queue中,此时Reference状态为ENQUEUED,Reference.queue = ReferenceENQUEUED */ /* 当从queue里面取出该元素,则变为INACTIVE,Reference.queue = Reference.NULL */ Reference reference1 = queue.remove(); System.out.println(reference1);
The above is the detailed content of Detailed explanation of Java Reference source code analysis code. For more information, please follow other related articles on the PHP Chinese website!