Maison  >  Article  >  Java  >  Explication détaillée du code d'analyse du code source de référence Java

Explication détaillée du code d'analyse du code source de référence Java

高洛峰
高洛峰original
2017-03-19 15:37:561816parcourir

@(Java)[Référence]

Analyse du code source de référence Java

Référenceobjet encapsule les références à d'autres objets et peut être utilisé comme des objets ordinaires sous certains. restrictions, l'interaction avec le ramasse-miettes est prise en charge. Autrement dit, vous pouvez utiliser l'objet Reference pour faire référence à d'autres objets, mais il sera quand même recyclé par le ramasse-miettes à la fin. Les programmes doivent parfois être avertis après le recyclage d'un objet pour informer l'objet d'un changement d'accessibilité.
Java propose quatre types de références différents. Les niveaux de référence de haut en bas sont FinalReference, SoftReference, WeakReference et PhantomReference. Parmi eux, FinalReference n'est pas disponible pour un usage externe. Chaque type correspond à un niveau d'accessibilité différent.

Introduction

Référence forte FinalReference

Une référence forte signifie qu'il existe une référence directement accessible dans le programme sans passer d'objet de référence, tel que Object obj = new Object();, obj est une référence forte.

SoftReference

Référence logicielle, pas une référence forte, mais accessible via un objet de référence logicielle. Pour les objets de référence logicielle, le garbage collector décidera de recycler l'objet pointé par la référence logicielle uniquement lorsque la mémoire est insuffisante (avant qu'une exception MOO ne soit levée). Les références logicielles sont souvent utilisées pour implémenter des caches sensibles à la mémoire.

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

Référence faible WeakReference

Référence faible, référence non forte et référence douce, mais accessible via un objet de référence faible. Un objet faiblement référencé sera recyclé tant qu'il sera découvert par le garbage collector, que la mémoire soit suffisante ou non. Pour les applications réelles, voir WeakHashMap, etc.

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

Référence virtuellePhantomReference

Référence virtuelle, cette référence doit être utilisée avec la file d'attente de référence (ReferenceQueue), et est généralement utilisée pour suivre les actions de recyclage du garbage collector, telles que lorsqu'un objet est recyclé Quand , la méthode finalize de l'objet sera appelée. Cette action peut être réalisée en utilisant des références virtuelles, ce qui est également plus sûr.

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

ReferenceQueue

En tant que membre de la référence, cette file d'attente peut être utilisée en combinaison avec les trois types de référence ci-dessus. La fonction de cette file d'attente est la suivante : lors de la création d'une référence, inscrivez-vous. la Queue to the Reference , lorsque l'objet référencé par la Reference est recyclé par le garbage collector, la Reference sera placée dans la file d'attente, ce qui équivaut à un mécanisme de notification.
Exemple de démonstration 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);

Analyse du code source

Référence et ReferenceQueue

Il existe plusieurs attributs importants dans la référence

// 用于保存对象的引用,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;

StatusGraph

详解Java Reference源码分析代码

Classe interne ReferenceHandler

ReferenceHandler est une classe interne statique de Reference, utilisée pour implémenter des instances de référence dans la file d'attente en attente Ajouté à différents ReferenceQueue à tour de rôle (en fonction de la file d'attente dans Reference). Les éléments en attente sont ajoutés par le GC.
Remarque : la file d'attente en attente est verrouillée ici. Personnellement, je pense que c'est parce que le thread GC peut s'exécuter en même temps que le thread où se trouve le ReferenceHandler, par exemple lorsque le GC utilise la collection simultanée CMS.
Comme indiqué dans le code suivant

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

L'attribut 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

appellera uniquement cette méthode via Reference, qui est utilisée pour transférer la référence Mettez-le dans la file d'attente actuelle

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

Le processus d'exécution spécifique

Utilisation de l'exemple ci-dessus Demo1 comme analyse

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn