Java Reference 源码分析

Reference对象封装了其它对象的引用,可以和普通的对象一样操作,在一定的限制条件下,支持和垃圾收集器的交互。即可以使用Reference对象来引用其它对象,但是最后还是会被垃圾收集器回收。程序有时候也需要在对象回收后被通知,以告知对象的可达性发生变更。
  Java提供了四种不同类型的引用,引用级别从高到低分别为FinalReference,SoftReference,WeakReference,PhantomReference。其中FinalReference不对外提供使用。每种类型对应着不同级别的可达性。

简介

强引用FinalReference

强引用指的是,程序中有直接可达的引用,而不需要通过任何引用对象,如Object obj = new Object();中,obj为强引用。

软引用SoftReference

软引用,非强引用,但是可以通过软引用对象来访问。软引用的对象,只有在内存不足的时候(抛出OOM异常前),垃圾收集器会决定回收该软引用所指向的对象。软引用通常用于实现内存敏感的缓存。

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

弱引用WeakReference

弱引用,非强引用和软引用,但是可以通过弱引用对象来访问。弱引用的对象,不管内存是否足够,只要被垃圾收集器发现,该引用的对象就会被回收。实际的应用见WeakHashMap等。

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

虚引用PhantomReference

虚引用,该引用必须和引用队列(ReferenceQueue)一起使用,一般用于实现追踪垃圾收集器的回收动作,比如在对象被回收的时候,会调用该对象的finalize方法,在使用虚引用可以实现该动作,也更加安全。

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

ReferenceQueue

该队列作为引用中的一员,可以和上述三种引用类型组合使用,该队列的作用是:创建Reference时,将Queue注册到Reference中,当该Reference所引用的对象被垃圾收集器回收时,会将该Reference放到该队列中,相当于一种通知机制。
示例 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);

源码分析

ReferenceReferenceQueue

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

ReferenceHandler作为Reference的静态内部类,用于实现将pending队列里面的Reference实例依次添加到不同的ReferenceQueue中(取决于Reference里面的queue)。该pending的元素由GC负责加入。
注:这里对pending队列进行加锁,个人认为是因为GC线程可能和ReferenceHandler所在的线程并发执行,如GC采用CMS并发收集的时候。
如下代码所示

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

// 用于标识没有注册Queuestatic 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=nullWeakReference reference = new WeakReference(new Object(), queue);
System.out.println(reference);// 当GC执行后,由于是虚引用,所以回收该object对象,并且置于pending上,此时reference的状态为PENDINGSystem.gc();/* ReferenceHandler从pending中取下该元素,并且将该元素放入到queue中,此时Reference状态为ENQUEUED,Reference.queue = ReferenceENQUEUED *//* 当从queue里面取出该元素,则变为INACTIVE,Reference.queue = Reference.NULL */Reference reference1 = queue.remove();
System.out.println(reference1);

应用 - WeakHashMap

WeakHashMap在使用上和HashMap类型,都是Hash + 链表解决冲突,唯一不同点在于前者的Key是使用虚引用来实现的,即当进行垃圾回收的时候,就是被回收掉,此时WeakHashMap会在下次操作的时候,根据被回收掉的Key,从Map里面移除掉。

Entry

当创建Entry的时候,会注册进当前Map属性的queue,当key被回收后,则该Entry会被放入到queue中,每当操作Map的时候,才会将原有的Value清除掉。(由expungeStaleEntries方法来进行,并且没有启动一个单独的线程来处理,没有必要,这样子简化了逻辑以及避免锁的开销)

// 外部WeakHashMap属性private final ReferenceQueue<Object> queue = new ReferenceQueue<>();/* 这里采用了集成WeakReference而不是直接使用,是因为当被回收的时候,具体的Key是不知道的,这里需要往WeakReference额外加入一些属性,以便在被回收后通知时,能够定位到具体的Key/value */private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {    // 这里属性不能加入key,否则会导致存在强引用而不能被视为WeakReference回收掉
    V value;    int hash;
    Entry<K,V> next;    
    Entry(Object key, V value,
          ReferenceQueue<Object> queue,          int hash, Entry<K,V> next) {        super(key, queue);        this.value = value;        this.hash  = hash;        this.next  = next;
    }    // ...}
时间: 2024-08-07 00:07:06

Java Reference 源码分析的相关文章

JAVA Collection 源码分析(一)之ArrayList

到今天为止,差不多已经工作一年了,一直在做的是javaweb开发,一直用的是ssh(sh)别人写好的框架,总感觉自己现在高不成低不就的,所以就像看看java的源码,顺便学习一下大牛的思想和架构,read and write一直是提高自己编程水平的不二法门,写博客只是记录自己的学习历程,方便回顾,写的不好的地方,请多多包含,不喜勿喷,好了废话少说,现在让我们开始我们的历程把,Let's go!!!!!!!! 想看源码无从下手,不知道有没有跟我一样感觉的人们,今天用Intellij发现了可以找出类与

JAVA Collection 源码分析(二)之SubList

昨天我们分析了ArrayList的源码,我们可以看到,在其中还有一个类,名为SubList,其继承了AbstractList. // AbstractList类型的引用,所有继承了AbstractList都可以传进来 private final AbstractList<E> parent; // 这个是其实就是parent的偏移量,从parent中的第几个元素开始的 private final int parentOffset; private final int offset; int s

Java 集合源码分析(一)HashMap

目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 ConcurrentHashMap 6. JDK 1.8 的 ConcurrentHashMap 7. 最后补充一下 HashMap 中的一些属性和方法 附:更这个系列感觉自己像是又挖了一个坑??,不过趁自己刚好工作不太忙,有空闲期,静下心来研究学习源码也是一件很值得做的事,自己尽量会把这个坑填完??.

超赞!推荐一个专注于Java后端源码分析的Github项目!

大家好,最近有小伙伴们建议我把源码分析文章及源码分析项目(带注释版)放到github上,这样小伙伴们就可以把带中文注释的源码项目下载到自己本地电脑,结合源码分析文章自己本地调试,总之对于学习开源项目源码会更方便. 因此下面提供[源码笔记]的Github地址,若您觉得不错,欢迎Star点亮哦: Github主页:https://github.com/yuanmabiji 源码分析文章:https://github.com/yuanmabiji/Java-SourceCode-Blogs Sprin

[Java] HashMap源码分析

1.概述 Hashmap继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口.它的key.value都可以为null,映射不是有序的. Hashmap不是同步的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Collections.synchronizedMap(new HashMap()); (除了不同步和允许使用 null 之

Java - LinkedList源码分析

java提高篇(二二)---LinkedList 一.概述 LinkedList与ArrayList一样实现List接口,只是ArrayList是List接口的大小可变数组的实现,LinkedList是List接口链表的实现.基于链表实现的方式使得LinkedList在插入和删除时更优于ArrayList,而随机访问则比ArrayList逊色些. LinkedList实现所有可选的列表操作,并允许所有的元素包括null. 除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾

Java - ArrayList源码分析

java提高篇(二一)-----ArrayList 一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小. 每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小.默认初始容量为10.随着ArrayList中元素的增加,它的容量也会不断的自动增长.在每次添加新的元素时,Array

java 集合类源码分析--arrays

本文介绍一下java集合相关类arryas类的内容 .Arrays.sort()数组排序 Java Arrays中提供了对所有类型的排序.其中主要分为Primitive(8种基本类型)和Object两大类. 基本类型:采用调优的快速排序: 对象类型:采用改进的归并排序. 1.对于基本类型源码分析如下(以int[]为例): public static void sort(int[] a, int fromIndex, int toIndex) { //进行数组的界限检查 rangeCheck(a.

Java集合源码分析(四)Vector&lt;E&gt;

Vector<E>简介 Vector也是基于数组实现的,是一个动态数组,其容量能自动增长. Vector是JDK1.0引入了,它的很多实现方法都加入了同步语句,因此是线程安全的(其实也只是相对安全,有些时候还是要加入同步语句来保证线程的安全),可以用于多线程环境. Vector没有丝线Serializable接口,因此它不支持序列化,实现了Cloneable接口,能被克隆,实现了RandomAccess接口,支持快速随机访问. Vector<E>源码 如下(已加入详细注释): /*