ホームページ  >  記事  >  Java  >  Javaリファレンスのソースコード解析コードの詳細説明

Javaリファレンスのソースコード解析コードの詳細説明

高洛峰
高洛峰オリジナル
2017-03-19 15:37:561771ブラウズ

@(Java)[参考]

Java リファレンス ソース コード分析

Referenceobject は、他のオブジェクトへの参照をカプセル化し、特定の制限の下で、通常のオブジェクトと同様に操作できます。ガベージ コレクターとの対話をサポートします。つまり、Reference オブジェクトを使用して他のオブジェクトを参照できますが、最終的にはガベージ コレクターによってリサイクルされます。プログラムは、オブジェクトがリサイクルされた後に、到達可能性の変更をオブジェクトに通知する必要がある場合があります。
Java は、高いレベルから低いレベルまで、FinalReference、SoftReference、WeakReference、および PhantomReference の 4 つの異なるタイプの参照を提供します。このうち、FinalReference は外部利用できません。各タイプは、異なるレベルのアクセシビリティに対応します。

はじめに

強参照 FinalReference

強参照とは、参照オブジェクトを渡さずにプログラム内に直接到達可能な参照があることを意味します。たとえば、Object obj = new Object(); です。 。

SoftReference

ソフト参照。強参照ではありませんが、ソフト参照オブジェクトを通じてアクセスできます。ソフト参照オブジェクトの場合、ガベージ コレクターは、メモリが不足している場合 (OOM 例外がスローされる前) にのみ、ソフト参照が指すオブジェクトをリサイクルすることを決定します。ソフト参照は、メモリに依存するキャッシュを実装するためによく使用されます。

SoftReference<Object> softRef = new SoftReference<Object>(new Object());

WeakReference

弱参照、非強参照、ソフト参照ですが、弱参照オブジェクトを通じてアクセスできます。弱参照オブジェクトは、メモリが十分であるかどうかに関係なく、ガベージ コレクターによって検出される限りリサイクルされます。実際の応用についてはWeakHashMapなどをご覧ください。

WeakReference<Object> weakRef = new WeakReference<Object>(new Object());

仮想参照 PhantomReference

この参照は、参照キュー (ReferenceQueue) と一緒に使用する必要があります。これは通常、オブジェクトがリサイクルされるとき、ファイナライズされるときなど、ガベージ コレクターのリサイクル アクションを追跡するために使用されます。オブジェクトのメソッドが呼び出されます。このアクションは仮想参照を使用して実現できますが、これもより安全です。

Object obj = new Object();
ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
PhantomReference<Object> phantom = new PhantomReference<Object>(obj, refQueue);

ReferenceQueue

このキューは、リファレンスのメンバーとして、上記の 3 つのリファレンス タイプと組み合わせて使用​​できます。 このキューの機能は次のとおりです。 Reference を作成すると、Queue が Reference に登録されます。 Reference によって参照されるのは、ガベージ コレクターがリサイクルするとき、Reference をキューに入れます。これは、通知メカニズムに相当します。
サンプルデモ1:

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);

ソースコード分析

ReferenceとReferenceQueue

Referenceの静的内部クラスとしての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;

StatusFigure

详解Java Reference源码分析代码

内部クラスReferenceHandler

ReferenceHandler内にはいくつかの重要な属性がありますクラス、保留キュー内の Reference インスタンスを異なる ReferenceQueue に順番に追加するために使用されます (Reference 内のキューに応じて)。保留中の要素は GC によって追加されます。
注: ここで保留キューがロックされているのは、GC が CMS 同時コレクションを使用している場合など、GC スレッドが ReferenceHandler が配置されているスレッドと並行して実行される可能性があるためだと個人的には考えています。
次のコードに示すように

// 此线程在静态块中启动,即一旦使用了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);
        }
    }
}

ReferenceQueue属性

// 用于标识没有注册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;

ReferenceQueue.enqueue

このメソッドは、Referenceを現在のキューに入れるためにReferenceを通してのみ呼び出されます

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;
        }
    }
}

ReferenceQueue.remove

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;
        }
    }
}

具体的な実行プロセス

上記の例 Demo1 を分析として使用する

// 创建一个引用队列
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);

以上がJavaリファレンスのソースコード解析コードの詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。