浅析Java Reference

  Java1.2引入了新的概念——Reference,在这之前都是默认的强引用,即Strong Reference。在GC过程中,只要从GC Roots通过强引用有路径可达则说明接下来的程序还可能用到,就不能回收,反之则回收。
  但是这种方式比较死板,为了增加垃圾回收的灵活性便有了java.lang.ref类库,里头包含最重要的抽象类Reference,及其三个继承类:SoftReference(软引用)、WeakReference(弱引用)和PhantomReference(幻影引用)。当垃圾回收器正在考察的对象只能通过上述三个中某个Reference对象才可获得时,这三个Reference派生类会为GC提供不同的指示:

  1. 当JVM报告内存不足的时候,所有只被SoftReference所指向的对象会被GC回收,否则不会回收;
  2. 当GC发现一个对象只能通过弱引用对象可达,将会释放WeakReference所引用的对象。
  3. 当GC发现一个对象只能通过幻影引用对象可达,将会将PhantomReference对象插入与其关联的ReferenceQueue队列中(PhantomReference对象的初始化必须有一个ReferenceQueue队列)。而此时PhantomReference所指向的对象只是执行了finalize方法,但是对象本身并没有被GC回收,要等到ReferenceQueue被真正的处理(如调用remove方法)后才会被回收。

那么上述功能是如何完成的呢?下面借助JDK源码简单分析下:
  在Reference类中有个域referent用来保存所指对象的引用:

private T referent;

/* -- Constructors -- */

Reference(T referent) {
    this(referent, null);
}

Reference(T referent, ReferenceQueue<? super T> queue) {
    this.referent = referent;
    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

在初始化Reference对象时,referent会指向传递进来的对象。在GC时如果发现目标对象只有Reference对象中的referent指向时,就会根据上述三条规则执行回收策略。我们看到第二个构造函数另外包括一个ReferenceQueue对象,我们很多后续操作都需要依靠这个对象完成,那这个对象是干嘛用的呢?
  GC回收Reference所指对象后(这里实际指referent所指向的对象,另外PhantomReference是在回收前,finalize后),将Reference添加到与其绑定的ReferenceQueue中,程序通过调用ReferenceQueue的remove方法或poll方法来感知reference被GC回收的事件。下面是一段Reference的源码:

static private class Lock { };
private static Lock lock = new Lock();

/* List of References waiting to be enqueued.  The collector adds
* References to this list, while the Reference-handler thread removes
* them.  This list is protected by the above lock object.
*/
 private static Reference pending = null;

 /* High-priority thread to enqueue pending References
  */
 private static class ReferenceHandler extends Thread {

     ReferenceHandler(ThreadGroup g, String name) {
         super(g, name);
     }

     public void run() {
         for (;;) {

             Reference r;
             synchronized (lock) {
                 if (pending != null) {
                     r = pending;
                     Reference rn = r.next;
                     pending = (rn == r) ? null : rn;
                     r.next = r;
                 } else {
                     try {
                         lock.wait();
                     } catch (InterruptedException x) { }
                     continue;
                 }
             }

             // Fast path for cleaners
             if (r instanceof Cleaner) {
                 ((Cleaner)r).clean();
                 continue;
             }

             ReferenceQueue q = r.queue;
             if (q != ReferenceQueue.NULL) q.enqueue(r);
         }
     }
 }

 static {
     ThreadGroup tg = Thread.currentThread().getThreadGroup();
     for (ThreadGroup tgn = tg;
          tgn != null;
          tg = tgn, tgn = tg.getParent());
     Thread handler = new ReferenceHandler(tg, "Reference Handler");
     /* If there were a special system-only priority greater than
      * MAX_PRIORITY, it would be used here
      */
     handler.setPriority(Thread.MAX_PRIORITY);
     handler.setDaemon(true);
     handler.start();
 }

ReferenceHandler这个线程在Reference类初始化的时候就作为守护进程启动。注意这里有一个Reference类型的成员变量pending,ReferenceHandler会循环检测pending是否为空,如果为空就挂起等待。当Reference所指向的对象在GC阶段被回收,就会被添加到pending上,然后唤醒ReferenceHandler,将Reference加入ReferenceQueue中。现在就可以利用ReferenceQueue做一些后续操作了,就拿经典的WeakHashMap来说一说,先看看它的Entry:

 private final ReferenceQueue<Object> queue = new ReferenceQueue<>();

/**
 * The entries in this hash table extend WeakReference, using its main ref
 * field as the key.
 */
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
    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);  //用key来初始化referent
        this.value = value;
        this.hash  = hash;
        this.next  = next;
    }
    //省略
}

Entry继承了WeakReference,所以它有了弱引用的性质。这里让弱引用指向key值所指的对象,所以当没有其他更强的引用指向key所指对象时,目标对象会被GC掉,并将持有这个key的Entry加入queue所指的ReferenceQueue中去。既然key值都冇得了,自然这个Entry就没有存在的意义了,所以得找个机会把它咔嚓掉。WeakHashMap把这个时间点设置到了每一次调用getTable或size方法时,首先进行一次清扫工作,而这个清扫工作正是依赖于引用队列queue中记录的引用对象。

HashMap、Hashtable和HashSet是Java中常用的集合类。 HashMap和Hashtable都实现了Map接口,用于存储键值对。它们的主要区别在于线程安全性和同步性。HashMap是非线程安全的,而Hashtable是线程安全的。这意味着多个线程可以同时访问Hashtable,但不能同时访问HashMap。此外,HashMap允许存储null键值对,而Hashtable不允许。Java 5引入的ConcurrentHashMap可以作为Hashtable的替代,它在扩展性方面更好。 HashSet实现了Set接口,用于存储无序、不重复的元素。它底层使用HashMap来存储数据,HashSet存储对象而不存储键值对。 总结一下,HashMap和Hashtable都是用于存储键值对的,区别在于线程安全性和同步性。HashSet是用于存储不重复元素的集合。 引用: HashMap和Hashtable都实现了Map接口,主要的区别有:线程安全性,同步(synchronization),以及速度。HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。 HashMap是非synchronized,而Hashtable是synchronized,意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器。 HashMap可以通过下面的语句进行同步:Map m = Collections.synchronizeMap(hashMap); 引用:HashMap和Hashtable两个类都实现了Map接口,二者保存K-V对(key-value对);HashSet则实现了Set接口,性质类似于集合。 引用:HashSet:散列表,无序的,不会记录插入的顺序,实现了 Set 接口,以对象作为元素,拒绝接受重复的对象,底层依靠 HashMap来存储数据, 底层使用HashTable来保证元素的不重复性,实际上使用的是HashMap的一个实例。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java集合HashMap、HashTable、HashSet详解](https://blog.youkuaiyun.com/weixin_38166557/article/details/99230114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [浅析Java中Map与HashMap,Hashtable,HashSet的区别](https://download.youkuaiyun.com/download/weixin_38570296/12813847)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java:HashMap、HashSet、HashTable](https://blog.youkuaiyun.com/weixin_48493408/article/details/123465326)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值