• WeakHashMap和Java引用类型详细解析


    WeakHashMap是种弱引用的HashMap,这是说,WeakHashMap里的key值如果没有外部强引用,在垃圾回收之后,WeakHashMap的对应内容也会被移除掉。

    1.1 Java的引用类型

    在讲解WeakHashMap之前,我们需要了解Java中引用的相关类:

    ReferenceQueue,引用队列,与某个引用类绑定,当引用死亡后,会进入这个队列。

    HardReference,强引用,任何以类似String str=new String()建立起来的引用,都是强引用。在str指向另一个对象或者null之前,该String对象都不会被GC(Garbage Collector垃圾回收器)回收;

    WeakReference,弱引用,可以通过java.lang.ref.WeakReference来建立,当GC要求回收对象时,它不会阻止对象被回收,该对象会立刻被回收;

    SoftReference,软引用,可以通过java.lang.ref.SoftReference来建立,和弱引用一样,当GC要求回收时,它不会阻止对象被回收,但不同的是该对回收过程会被延迟,必须要等到JVM heap内存不够用,接近产生OutOfMemory错误时,才会回收;

    PhantomReference,虚引用,可以通过java.lang.ref.PhantomPeference来建立,这种类型的引用很特别,大多数时间,无法通过它拿到其引用的对象,但是,当这个对象死亡的时候,该引用还是会进入ReferenceQueue队列。

    下面提供一个例子来分别说明它们的作用:

    ReferenceQueue<Ref> queue = new ReferenceQueue<Ref>();

    // 创建一个弱引用

    WeakReference<Ref> weak = new WeakReference<Ref>(new Ref("Weak"),queue);

    // 创建一个虚引用

    PhantomReference<Ref> phantom = new PhantomReference<Ref>(new Ref(

    "Phantom"), queue);

    // 创建一个软引用

    SoftReference<Ref> soft = new SoftReference<Ref>(new Ref("Soft"),queue);

    System.out.println("引用内容:");

    System.out.println(weak.get());

    System.out.println(phantom.get());

    System.out.println(soft.get());

    System.out.println("被回收的引用:");

    for (Reference r = null; (r = queue.poll()) != null;) {

    System.out.println(r);

    }

    Ref这个类是个自定义的测试类,源码如下所示:

    class Ref {

    Object v;

    Ref(Object v) {

    this.v = v;

    }

    public String toString() {

    return this.v.toString();

    }

    }

    在这个例子里,分别创建了弱引用、虚引用和软引用,get()方法用于获取它们引用的Ref对象,可以注意到,Ref对象在外部并没有任何引用,所以,在某个时间点,GC应当会回收对象。来看看代码执行的结果:

    引用内容:

    Weak

    null

    Soft

    被回收的引用:

    可以看到,弱引用和软引用的对象还是可达的,但是虚引用是不可达的。被回收的引用没有内容,说明GC还没有回收它们。

    这证实了虚引用的性质

    虚引用非常弱,以至于它自己也找不到自己的引用内容。

    对之前的代码进行改造,在输出内容前加入代码:

    // 强制垃圾回收

    System.gc();

    再执行一次,得到结果:

    引用内容:

    null

    null

    Soft

    被回收的引用:

    java.lang.ref.WeakReference@3b764bce

    java.lang.ref.PhantomReference@759ebb3d

    现在可达的引用只剩下Soft了,引用队列里多出了两条引用,说明WeakReference和PhantomReference的对象被回收。

    再修改一次代码,让WeakPeference和PhantomReference去引用一个强引用对象:

    Ref wr = new Ref("Hard");

    WeakReference<Ref> weak = new WeakReference<Ref>(wr, queue);

    PhantomReference<Ref> phantom = new PhantomReference<Ref>(wr, queue);

    输出结果如下所示:

    引用内容:

    Hard

    null

    Soft

    被回收的引用:

    这证实了弱引用的性质

    弱引用的对象,如果没有被强引用,在垃圾回收后,引用对象会不可达。

    1.2 WeakHashMap实现方式

    WeakHashMap利用了ReferenceQueue和WeakReference来实现它的核心功能:当key值没有强引用的时候,从WeakHashMap里移除。

    先来看看WeakHashMap的键值对实体类WeakHashMap.Entry的实现:

     private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {

            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;

            }

        ...

    }

    关键注意两处:

    1、Entry继承自WeakReference;

    2、Entry本身没有保存key值,而是把key作为WeakReference的引用对象交给了super构造。

    这说明,Entry是个针对key值的弱引用。

    WeakHashMap实现清除陈旧实体的方法是expungStaleEntries(),其源码实现如下:

    private void expungeStaleEntries() {

        //遍历引用队列,找到每一个被GC收集的对象

            for (Object x; (x = queue.poll()) != null; ) {

                synchronized (queue) {

                    @SuppressWarnings("unchecked")

                        Entry<K,V> e = (Entry<K,V>) x;

                    int i = indexFor(e.hash, table.length);

                    //从链表里移除该实体

                    Entry<K,V> prev = table[i];

                    Entry<K,V> p = prev;

                    while (p != null) {

                        Entry<K,V> next = p.next;

                        if (p == e) {

                            if (prev == e)

                                table[i] = next;

                            else

                                prev.next = next;

                            //帮助GC执行

                            e.value = null;

                            size--;

                            break;

                        }

                        prev = p;

                        p = next;

                    }

                }

            }

        }

    expungStaleEntries()方法会在resize、put、get、forEach方法里被调用。

  • 相关阅读:
    队列(顺序存储结构)
    2015计划
    iframe子窗口父窗口方法调用和元素获取
    Ajax关于重定向
    Java国际化资源文件的选择
    eclipse自动部署web应用程序到tomcat webapps
    从给定字符串结尾获取指定字节长度的字符串
    Winform的一些不知道啥东西
    C# 2008核心编程 2013-09-14
    C# 2008核心编程 2013-09-10
  • 原文地址:https://www.cnblogs.com/anrainie/p/6289761.html
Copyright © 2020-2023  润新知