java的WeakHashMap详细介绍

      在上一篇文章中介绍了Java的四种对象引用,这篇文章介绍一个java中相关的具体应用WeakHashMap。

      WeakHashMap是基于弱引用实现的哈希表,与HashMap在操作上基本相同,WeakHashMap最大的特点是对于哈希表中的每个Key,如果除了自身对Key有引用外,此Key没有其他引用,那么该Key对应的键值对会被表自动移除,这个行为取决于垃圾回收器的动作,只有在垃圾回收器清除该Key的弱引用之后,该键值对才会被自动移除。

示例1:

public class test {
  public static void main(String[] args) throws InterruptedException {
	  Map<String, String> weakHashMap=new WeakHashMap<String,String>();  
	  String str=new String("test");
	  weakHashMap.put(str,"1");
	  System.out.println("size1:"+weakHashMap.size());
	  str=null;
	  System.gc();
	  Thread.sleep(2000);
	  System.out.println("size2:"+weakHashMap.size());
  }
}

程序输出结果

size1:1
size2:0

当我们将str置为null时,此时仅有weakHashMap维持有对new String(“test”)这个对象的引用,之后weakHashMap就会移除该键值对,那么weakHashMap的移除过程是如何触发的呢?我们可以一步一步的测试一下。

示例2:

public class test {
  public static void main(String[] args){
	  List<WeakHashMap<byte[][],byte[][]>> mapList=new ArrayList<WeakHashMap<byte[][],byte[][]>>();
	  int len=1024*10;
	  for(int i=1;i<1000;i++){
		  WeakHashMap<byte[][],byte[][]> map=new WeakHashMap<byte[][],byte[][]>();
		  map.put(new byte[len][len],new byte[len][len]);
		  mapList.add(map);
          System.gc();
		  System.out.println(i);
	  }
  }
}

在我本机上运行实例2,循环到第22次就会报OutOfMemoryErro(如果你的没有报这个错误,可以考虑把len值调大一些),这说明我们申请的堆没有及时得到释放,你可能会感到疑惑,我们明明已经调用了System.gc(),weakHashMap会自动移除没有其他引用的键值对,该键值对关联的堆就会得到释放,我们再次申请分配堆的时候应该有足够的堆资源,怎么还会报这个错误呢?我们再看下个例子。

示例3:

public class test {
	  public static void main(String[] args){
		  List<WeakHashMap<byte[][],byte[][]>> mapList=new ArrayList<WeakHashMap<byte[][],byte[][]>>();
		  int len=1024*10;
		  for(int i=1;i<1000;i++){
			  WeakHashMap<byte[][],byte[][]> map=new WeakHashMap<byte[][],byte[][]>();
			  map.put(new byte[len][len],new byte[len][len]);
			  mapList.add(map);
	          System.gc();
//增加的代码
	          for(WeakHashMap tmp:mapList){
	        	    tmp.size();
	          }
			  System.out.println(i);
		  }
	  }
	}

运行实例3,我们可以发现不会报错了,循环能够正常结束,说明申请的堆得到了及时的释放,与实例2相比实例3对已经在列表中的WeakHashMap调用了size()这一个方法,我们看一下WeakHashMap的size方法源码

    public int size() {
        if (size == 0)
            return 0;
        expungeStaleEntries();
        return size;
}

在这个方法中调用了expungeStaleEntries()方法

 private void expungeStaleEntries() {
        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;
                        // Must not null out e.next;
                        // stale entries may be in use by a HashIterator
                        e.value = null; // Help GC
                        size--;
                        break;
                    }
                    prev = p;
                    p = next;
                }
            }
        }
    }

而这个正是WeakHashMap移除除键值所调用的方法,只不过当你启动垃圾回收器之后这个方法并不会自动触发,只有接下来对WeakHashMap有进行操作才会触发,翻看WeakHashMap的源码,你会发现只有size(),resize(),getTable()这个三个方法中调用了expungeStaleEntries()方法,而这三个方法在WeakHashMap的所有操作方法中至少有一个会调用。

       我们开头说WeakHashMap是基于弱引用的,这个弱引用就体现在Key值上,我们查看WeakHashMap的键值对存储结构Entry<K,V>

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
        V value;
        final int hash;
        Entry<K,V> next;

        /**
         * Creates new entry.
         */
        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;
        }
    ……省略……
}

可以看到Entry<K,V>继承至弱引用WeakReference<Object>,每次构造一个新的Entry时,Key值对应的对象都会被构造为一个WeakReference,这样当外部没有对Key值的引用时,该Key指向的对象资源便会被回收,并将弱引用放到ReferenceQueue中,注意这里也仅仅只是回收Key指向的对象资源,而键值对的删除以及Value所指向对象的处理都在上面提到的expungeStaleEntries函数里。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值