有必要了解一下Java 引用類型原理

Reference

我們先看下Reference.java中的幾個字段

public abstract class Reference<T> {
    //引用的對象
    private T referent;
    //回收隊列
    vol00000atile ReferenceQueue<? super T> queue;
     //當該引用被加入到queue中的時候,該字段被設置爲queue中的下一個元素,以形成鏈表結構
    volatile Reference next;
    //在GC時,JVM底層會維護一個叫DiscoveredList的鏈表,存放的是Reference對象,discovered字段指向的就是鏈表中的下一個元素,由JVM設置
    transient private Reference<T> discovered;
    //進行線程同步的鎖對象
    static private class Lock { }
    private static Lock lock = new Lock();
    //等待加入queue的Reference對象,在GC時由JVM設置,會有一個java層的線程(ReferenceHandler)源源不斷的從pending中提取元素加入到queue
    private static Reference<Object> pending = null;
}

一個Reference對象的生命週期如下:

在這裏插入圖片描述

主要分爲Native層和Java層兩個部分。

Native層在GC時將需要被回收的Reference對象加入到DiscoveredList中(代碼在referenceProcessor.cpp中process_discovered_references方法),然後將DiscoveredList的元素移動到PendingList中(代碼在referenceProcessor.cpp中enqueue_discovered_ref_helper方法),PendingList的隊首就是Reference類中的pending對象。

Java層的代碼:

private static class ReferenceHandler extends Thread {
         ...
        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
  }
static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                     //如果是Cleaner對象,則記錄下來,下面做特殊處理
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    //指向PendingList的下一個對象
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                   //如果pending爲null就先等待,當有對象加入到PendingList中時,jvm會執行notify
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // retry if waited
                    return waitForNotify;
                }
            }
        }
        ...

        // 如果時CLeaner對象,則調用clean方法進行資源回收
        if (c != null) {
            c.clean();
            return true;
        }
        //將Reference加入到ReferenceQueue,開發者可以通過從ReferenceQueue中poll元素感知到對象被回收的事件。
        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
 }

流程比較簡單:就是源源不斷的從PendingList中提取出元素,然後將其加入到ReferenceQueue中去,開發者可以通過從ReferenceQueue中poll元素感知到對象被回收的事件。

另外需要注意的是,對於Cleaner類型(繼承自虛引用)的對象會有額外的處理:在其指向的對象被回收時,會調用clean方法,該方法主要是用來做對應的資源回收,在堆外內存DirectByteBuffer中就是用Cleaner進行堆外內存的回收,這也是虛引用在java中的典型應用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章